20 General utilities library [utilities]

20.15 Metaprogramming and type traits [meta]

20.15.7 Transformations between types [meta.trans]

This subclause contains templates that may be used to transform one type to another following some predefined rule.

Each of the templates in this subclause shall be a TransformationTrait ([meta.rqmts]).

20.15.7.1 Const-volatile modifications [meta.trans.cv]

Table 45 — Const-volatile modifications
TemplateComments
template <class T>
struct remove_const;
The member typedef type shall name the same type as T except that any top-level const-qualifier has been removed. [ Example: remove_const_t<const volatile int> evaluates to volatile int, whereas remove_const_t<const int*> evaluates to const int*.  — end example ]
template <class T>
struct remove_volatile;
The member typedef type shall name the same type as T except that any top-level volatile-qualifier has been removed. [ Example: remove_volatile_t<const volatile int> evaluates to const int, whereas remove_volatile_t<volatile int*> evaluates to volatile int*.  — end example ]
template <class T>
struct remove_cv;
The member typedef type shall be the same as T except that any top-level cv-qualifier has been removed. [ Example: remove_cv_t<const volatile int> evaluates to int, whereas remove_cv_t<const volatile int*> evaluates to const volatile int*.  — end example ]
template <class T>
struct add_const;
If T is a reference, function, or top-level const-qualified type, then type shall name the same type as T, otherwise T const.
template <class T>
struct add_volatile;
If T is a reference, function, or top-level volatile-qualified type, then type shall name the same type as T, otherwise T volatile.
template <class T>
struct add_cv;
The member typedef type shall name the same type as add_const_t<add_volatile_t<T>>.

20.15.7.2 Reference modifications [meta.trans.ref]

Table 46 — Reference modifications
TemplateComments
template <class T>
struct remove_reference;
If T has type “reference to T1” then the member typedef type shall name T1; otherwise, type shall name T.
template <class T>
struct add_lvalue_reference;
If T names a referenceable type ([defns.referenceable]) then the member typedef type shall name T&; otherwise, type shall name T. [ Note: This rule reflects the semantics of reference collapsing ([dcl.ref]).  — end note ]
template <class T>
struct add_rvalue_reference;
If T names a referenceable type then the member typedef type shall name T&&; otherwise, type shall name T. [ Note: This rule reflects the semantics of reference collapsing ([dcl.ref]). For example, when a type T names a type T1&, the type add_rvalue_reference_t<T> is not an rvalue reference.  — end note ]

20.15.7.3 Sign modifications [meta.trans.sign]

Table 47 — Sign modifications
TemplateComments
template <class T>
struct make_signed;
If T names a (possibly cv-qualified) signed integer type ([basic.fundamental]) then the member typedef type shall name the type T; otherwise, if T names a (possibly cv-qualified) unsigned integer type then type shall name the corresponding signed integer type, with the same cv-qualifiers as T; otherwise, type shall name the signed integer type with smallest rank ([conv.rank]) for which sizeof(T) == sizeof(type), with the same cv-qualifiers as T.
Requires: T shall be a (possibly cv-qualified) integral type or enumeration but not a bool type.
template <class T>
struct make_unsigned;
If T names a (possibly cv-qualified) unsigned integer type ([basic.fundamental]) then the member typedef type shall name the type T; otherwise, if T names a (possibly cv-qualified) signed integer type then type shall name the corresponding unsigned integer type, with the same cv-qualifiers as T; otherwise, type shall name the unsigned integer type with smallest rank ([conv.rank]) for which sizeof(T) == sizeof(type), with the same cv-qualifiers as T.
Requires: T shall be a (possibly cv-qualified) integral type or enumeration but not a bool type.

20.15.7.4 Array modifications [meta.trans.arr]

Table 48 — Array modifications
TemplateComments
template <class T>
struct remove_extent;
If T names a type “array of U”, the member typedef type shall be U, otherwise T. [ Note: For multidimensional arrays, only the first array dimension is removed. For a type “array of const U”, the resulting type is const U.  — end note ]
template <class T>
struct remove_all_extents;
If T is “multi-dimensional array of U”, the resulting member typedef type is U, otherwise T.

Example:

// the following assertions hold:
assert((is_same_v<remove_extent_t<int>, int>));
assert((is_same_v<remove_extent_t<int[2]>, int>));
assert((is_same_v<remove_extent_t<int[2][3]>, int[3]>));
assert((is_same_v<remove_extent_t<int[][3]>, int[3]>));

 — end example ]

Example:

// the following assertions hold:
assert((is_same_v<remove_all_extents_t<int>, int>));
assert((is_same_v<remove_all_extents_t<int[2]>, int>));
assert((is_same_v<remove_all_extents_t<int[2][3]>, int>));
assert((is_same_v<remove_all_extents_t<int[][3]>, int>));

 — end example ]

20.15.7.5 Pointer modifications [meta.trans.ptr]

Table 49 — Pointer modifications
TemplateComments
template <class T>
struct remove_pointer;
If T has type “(possibly cv-qualified) pointer to T1” then the member typedef type shall name T1; otherwise, it shall name T.
template <class T>
struct add_pointer;
If T names a referenceable type ([defns.referenceable]) or a (possibly cv-qualified) void type then the member typedef type shall name the same type as remove_reference_t<T>*; otherwise, type shall name T.

20.15.7.6 Other transformations [meta.trans.other]

Table 50 — Other transformations
TemplateComments
template <size_t Len,
size_t Align
= default-alignment>
struct aligned_storage;
The value of default-alignment shall be the most stringent alignment requirement for any C++ object type whose size is no greater than Len ([basic.types]). The member typedef type shall be a POD type suitable for use as uninitialized storage for any object whose size is at most Len and whose alignment is a divisor of Align.
Requires: Len shall not be zero. Align shall be equal to alignof(T) for some type T or to default-alignment.
template <size_t Len,
class... Types>
struct aligned_union;
The member typedef type shall be a POD type suitable for use as uninitialized storage for any object whose type is listed in Types; its size shall be at least Len. The static member alignment_value shall be an integral constant of type size_t whose value is the strictest alignment of all types listed in Types.
Requires: At least one type is provided.
template <class T>
struct decay;
Let U be remove_reference_t<T>. If is_array_v<U> is true, the member typedef type shall equal remove_extent_t<U>*. If is_function_v<U> is true, the member typedef type shall equal add_pointer_t<U>. Otherwise the member typedef type equals remove_cv_t<U>. [ Note: This behavior is similar to the lvalue-to-rvalue ([conv.lval]), array-to-pointer ([conv.array]), and function-to-pointer ([conv.func]) conversions applied when an lvalue expression is used as an rvalue, but also strips cv-qualifiers from class types in order to more closely model by-value argument passing.  — end note ]
template <bool B, class T = void> struct enable_if; If B is true, the member typedef type shall equal T; otherwise, there shall be no member type.
template <bool B, class T, class F>
struct conditional;
If B is true, the member typedef type shall equal T. If B is false, the member typedef type shall equal F.
template <class... T> struct common_type; Unless this trait is specialized (as specified in Note B, below), the member type shall be defined or omitted as specified in Note A, below. If it is omitted, there shall be no member type. Each type in the parameter pack T shall be complete, void, or an array of unknown bound.
template <class T>
struct underlying_type;
The member typedef type shall name the underlying type of T.
Requires: T shall be a complete enumeration type ([dcl.enum])
template <class Fn,
class... ArgTypes>
struct result_of<
Fn(ArgTypes...)>;
If the expression INVOKE(declval<Fn>(), declval<ArgTypes>()...) is well formed when treated as an unevaluated operand (Clause [expr]), the member typedef type shall name the type decltype(INVOKE(declval<Fn>(), declval<ArgTypes>()...)); otherwise, there shall be no member type. Access checking is performed as if in a context unrelated to Fn and ArgTypes. Only the validity of the immediate context of the expression is considered. [ Note: The compilation of the expression can result in side effects such as the instantiation of class template specializations and function template specializations, the generation of implicitly-defined functions, and so on. Such side effects are not in the “immediate context” and can result in the program being ill-formed.  — end note ]
Requires: Fn and all types in the parameter pack ArgTypes shall be complete types, (possibly cv-qualified) void, or arrays of unknown bound.

Note: A typical implementation would define aligned_storage as:

template <size_t Len, size_t Alignment>
struct aligned_storage {
  typedef struct {
    alignas(Alignment) unsigned char __data[Len];
  } type;
};

 — end note ]

It is implementation-defined whether any extended alignment is supported ([basic.align]).

Note A: For the common_type trait applied to a parameter pack T of types, the member type shall be either defined or not present as follows:

  • If sizeof...(T) is zero, there shall be no member type.

  • If sizeof...(T) is one, let T0 denote the sole type constituting the pack T. The member typedef-name type shall denote the same type as decay_t<T0>.

  • If sizeof...(T) is two, let the first and second types constituting T be denoted by T1 and T2, respectively, and let D1 and D2 denote the same types as decay_t<T1> and decay_t<T2>, respectively.

    • If is_same_v<T1, D1> is false or is_same_v<T2, D2> is false, let C denote the same type, if any, as common_type_t<D1, D2>.

    • Otherwise, let C denote the same type, if any, as

      decay_t<decltype(false ? declval<D1>() : declval<D2>())>
      

      Note: This will not apply if there is a specialization common_type<D1, D2>.  — end note ]

    In either case, the member typedef-name type shall denote the same type, if any, as C. Otherwise, there shall be no member type. [ Note: When is_same_v<T1, T2> is true, the effect is equivalent to that of common_type<T1>.  — end note ]

  • If sizeof...(T) is greater than two, let T1, T2, and R, respectively, denote the first, second, and (pack of) remaining types constituting T. Let C denote the same type, if any, as common_type_t<T1, T2>. If there is such a type C, the member typedef-name type shall denote the same type, if any, as common_type_t<C, R...>. Otherwise, there shall be no member type.

Note B: Notwithstanding the provisions of [meta.type.synop], and pursuant to [namespace.std], a program may specialize common_type<T1, T2> for distinct types T1 and T2 such that is_same_v<T1, decay_t<T1>> and is_same_v<T2, decay_t<T2>> are each true. [ Note: Such specializations are needed when only explicit conversions are desired between the template arguments.  — end note ] Such a specialization need not have a member named type, but if it does, that member shall be a typedef-name for an accessible and unambiguous cv-unqualified non-reference type C to which each of the types T1 and T2 is explicitly convertible. Moreover, common_type_t<T1, T2> shall denote the same type, if any, as does common_type_t<T2, T1>. No diagnostic is required for a violation of this Note's rules.

Example: Given these definitions:

using PF1 = bool  (&)();
using PF2 = short (*)(long);

struct S {
  operator PF2() const;
  double operator()(char, int&);
  void fn(long) const;
  char data;
};

using PMF = void (S::*)(long) const;
using PMD = char  S::*;

the following assertions will hold:

static_assert(is_same_v<result_of_t<S(int)>, short>, "Error!");
static_assert(is_same_v<result_of_t<S&(unsigned char, int&)>, double>, "Error!");
static_assert(is_same_v<result_of_t<PF1()>, bool>, "Error!");
static_assert(is_same_v<result_of_t<PMF(unique_ptr<S>, int)>, void>, "Error!");
static_assert(is_same_v<result_of_t<PMD(S)>, char&&>, "Error!");
static_assert(is_same_v<result_of_t<PMD(const S*)>, const char&>, "Error!");

 — end example ]