타입 별명과 별명 템플릿

타입 별명과 별명 템플릿 (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 -

설명

  1. 타입 별명 선언은 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) 기록을 더 볼 수 있어요.