In C++, it is possible to create synonyms that can be used instead of a type name. This is achieved by creating a typedef
declaration. This is useful in several cases, such as creating shorter or more meaningful names for a type or names for function pointers. However, typedef
declarations cannot be used with templates to create template type aliases
. An std::vector<T>
, for instance, is not a type (std::vector<int>
is a type), but a sort of family of all types that can be created when the type placeholder T
is replaced with an actual type.
In C++11, a type alias is a name for another already declared type, and an alias template is a name for another already declared template. Both of these types of aliases are introduced with a new using
syntax.
- Create type aliases with the form
using identifier = type-id
as in the following examples:
using byte = unsigned char; using pbyte = unsigned char *; using array_t = int[10]; using fn = void(byte, double); void func(byte b, double d) { /*...*/ } byte b {42}; pbyte pb = new byte[10] {0}; array_t a{0,1,2,3,4,5,6,7,8,9}; fn* f = func;
- Create alias templates with the form
template<template-params-list> identifier = type-id
as in the following examples:
template <class T> class custom_allocator { /* ... */}; template <typename T> using vec_t = std::vector<T, custom_allocator<T>>; vec_t<int> vi; vec_t<std::string> vs;
For consistency and readability, you should do the following:
- Not mix
typedef
andusing
declarations for creating aliases. - Use the
using
syntax to create names of function pointer types.
A typedef
declaration introduces a synonym (or an alias in other words) for a type. It does not introduce another type (like a class
, struct
, union
, or enum
declaration). Type names introduced with a typedef
declaration follow the same hiding rules as identifier names. They can also be redeclared, but only to refer to the same type (therefore, you can have valid multiple typedef
declarations that introduce the same type name synonym in a translation unit as long as it is a synonym for the same type). The following are typical examples of typedef
declarations:
typedef unsigned char byte; typedef unsigned char * pbyte; typedef int array_t[10]; typedef void(*fn)(byte, double); template<typename T> class foo { typedef T value_type; }; typedef std::vector<int> vint_t;
A type alias declaration is equivalent to a typedef
declaration. It can appear in a block scope, class scope, or namespace scope. According to C++11 paragraph 7.1.3.2:
A typedef-name can also be introduced by an alias-declaration. The identifier following the using keyword becomes a typedef-name and the optional attribute-specifier-seq following the identifier appertains to that typedef-name. It has the same semantics as if it were introduced by the typedef specifier. In particular, it does not define a new type and it shall not appear in the type-id.
An alias-declaration is, however, more readable and more clear about the actual type that is aliased when it comes to creating aliases for array types and function pointer types. In the examples from the How to do it... section, it is easily understandable that array_t
is a name for the type array of 10 integers, and fn
is a name for a function type that takes two parameters of type byte
and double
and returns void
. That is also consistent with the syntax for declaring std::function
objects (for example, std::function<void(byte, double)> f
).
The driving purpose of the new syntax is to define alias templates. These are templates which, when specialized, are equivalent to the result of substituting the template arguments of the alias template for the template parameters in the type-id
.
It is important to take note of the following things:
- Alias templates cannot be partially or explicitly specialized.
- Alias templates are never deduced by template argument deduction when deducing a template parameter.
- The type produced when specializing an alias template is not allowed to directly or indirectly make use of its own type.