타입 별명과 별명 템플릿
타입 별명과 별명 템플릿 (Type alias, alias template)
길고 복잡한 타입을 매번 원래대로 적기 힘들 때, 그 타입을 가리키는 짧은 이름을 하나 만들어 두면 편해요. 이렇게 이미 정의된 타입을 가리키는 이름을 **타입 별명(type alias)**이라 하고, 타입들의 묶음(패밀리)을 가리키는 이름을 **별명 템플릿(alias template)**이라고 해요. typedef와 하는 일이 비슷하지만 문법이 더 직관적이죠.
출처: cppreference
본문
문법
별명 선언(alias declaration)은 다음 문법을 가져요.
using identifier attr(optional) = type-id; (1)
template< template-parameter-list >
using identifier attr(optional) = type-id; (2)
template< template-parameter-list > requires constraint
using identifier attr(optional) = type-id; (3) (since C++20)
| 구성 요소 | 설명 |
|---|---|
attr |
- |
identifier |
- |
template-parameter-list |
- |
constraint |
- |
type-id |
- |
설명
- 타입 별명 선언은
type-id가 나타내는 타입의 별명으로 쓸 수 있는 이름을 도입해요. 새 타입을 도입하지도, 기존 타입 이름의 의미를 바꾸지도 않아요. 타입 별명 선언과 typedef 선언 사이엔 차이가 없어요.
template<class T>
struct Alloc {};
template<class T>
using Vec = vector<T, Alloc<T>>; // type-id is vector<T, Alloc<T>>
Vec<int> v; // Vec<int> is the same as vector<int, Alloc<int>>
별명 템플릿을 특수화한 결과가 의존적 템플릿 id(dependent template-id)라면, 이후의 치환이 그 템플릿 id에 적용돼요.
template<typename...>
using void_t = void;
template<typename T>
void_t<typename T::foo> f();
f<int>(); // error, int does not have a nested type foo
별명 템플릿을 특수화해서 만들어지는 타입은 직접 또는 간접적으로 자기 자신의 타입을 사용하면 안 돼요.
template<class T>
struct A;
template<class T>
using B = typename A<T>::U; // type-id is A<T>::U
template<class T>
struct A { typedef B<T> U; };
B<short> b; // error: B<short> uses its own type via A<short>::U
별명 템플릿은 템플릿 템플릿 매개변수를 추론할 때 템플릿 인자 추론(template argument deduction)으로 절대 추론되지 않아요.
다른 템플릿 선언처럼, 별명 템플릿은 클래스 스코프나 네임스페이스 스코프에서만 선언할 수 있어요.
| 별명 템플릿 선언에 등장하는 람다 표현식의 타입은, 그 람다 표현식이 의존적이지 않더라도 템플릿의 인스턴스화마다 달라요. |
|---|
template<class T> using A = decltype([] {}); // A<int>와 A<char>는 서로 다른 클로저 타입을 가리켜요 |
참고
| 기능 테스트 매크로 | 값 | 표준 | 기능 |
|---|---|---|---|
| __cpp_alias_templates | 200704L | (C++11) |
별명 템플릿 |
키워드
using
예제
#include <iostream>
#include <string>
#include <type_traits>
#include <typeinfo>
// type alias, identical to
// typedef std::ios_base::fmtflags flags;
using flags = std::ios_base::fmtflags;
// the name 'flags' now denotes a type:
flags fl = std::ios_base::dec;
// type alias, identical to
// typedef void (*func)(int, int);
using func = void (*) (int, int);
// the name 'func' now denotes a pointer to function:
void example(int, int) {}
func f = example;
// alias template
template<class T>
using ptr = T*;
// the name 'ptr<T>' is now an alias for pointer to T
ptr<int> x;
// type alias used to hide a template parameter
template<class CharT>
using mystring = std::basic_string<CharT, std::char_traits<CharT>>;
mystring<char> str;
// type alias can introduce a member typedef name
template<typename T>
struct Container { using value_type = T; };
// which can be used in generic programming
template<typename ContainerT>
void info(const ContainerT& c)
{
typename ContainerT::value_type T;
std::cout << "ContainerT is `" << typeid(decltype(c)).name() << "`\n"
"value_type is `" << typeid(T).name() << "`\n";
}
// type alias used to simplify the syntax of std::enable_if
template<typename T>
using Invoke = typename T::type;
template<typename Condition>
using EnableIf = Invoke<std::enable_if<Condition::value>>;
template<typename T, typename = EnableIf<std::is_polymorphic<T>>>
int fpoly_only(T) { return 1; }
struct S { virtual ~S() {} };
int main()
{
Container<int> c;
info(c); // Container::value_type will be int in this function
// fpoly_only(c); // error: enable_if prohibits this
S s;
fpoly_only(s); // okay: enable_if allows this
}
가능한 출력:
ContainerT is `struct Container<int>`
value_type is `int`
더 알아보기
- typedef 선언은 타입의 별명을 만드는 또 다른 방법이에요. typedef 문서에서 비교해 볼 수 있어요.
- 네임스페이스 별명은 기존 네임스페이스의 별명을 만들어요.
- cppreference의 타입 별명 원문에서 결함 보고(defect report) 기록을 더 볼 수 있어요.