21 Metaprogramming library [meta]

21.3 Metaprogramming and type traits [type.traits]

21.3.1 General [type.traits.general]

Subclause [type.traits] describes components used by C++ programs, particularly in templates, to support the widest possible range of types, optimize template code usage, detect type related user errors, and perform type inference and transformation at compile time.
It includes type classification traits, type property inspection traits, and type transformations.
The type classification traits describe a complete taxonomy of all possible C++ types, and state where in that taxonomy a given type belongs.
The type property inspection traits allow important characteristics of types or of combinations of types to be inspected.
The type transformations allow certain properties of types to be manipulated.
All functions specified in [type.traits] are signal-safe ([support.signal]).

21.3.2 Requirements [meta.rqmts]

A Cpp17UnaryTypeTrait describes a property of a type.
It shall be a class template that takes one template type argument and, optionally, additional arguments that help define the property being described.
It shall be Cpp17DefaultConstructible, Cpp17CopyConstructible, and publicly and unambiguously derived, directly or indirectly, from its base characteristic, which is a specialization of the template integral_constant, with the arguments to the template integral_constant determined by the requirements for the particular property being described.
The member names of the base characteristic shall not be hidden and shall be unambiguously available in the Cpp17UnaryTypeTrait.
A Cpp17BinaryTypeTrait describes a relationship between two types.
It shall be a class template that takes two template type arguments and, optionally, additional arguments that help define the relationship being described.
It shall be Cpp17DefaultConstructible, Cpp17CopyConstructible, and publicly and unambiguously derived, directly or indirectly, from its base characteristic, which is a specialization of the template integral_constant, with the arguments to the template integral_constant determined by the requirements for the particular relationship being described.
The member names of the base characteristic shall not be hidden and shall be unambiguously available in the Cpp17BinaryTypeTrait.
A Cpp17TransformationTrait modifies a property of a type.
It shall be a class template that takes one template type argument and, optionally, additional arguments that help define the modification.
It shall define a publicly accessible nested type named type, which shall be a synonym for the modified type.
Unless otherwise specified, the behavior of a program that adds specializations for any of the templates specified in [type.traits] is undefined.
Unless otherwise specified, an incomplete type may be used to instantiate a template specified in [type.traits].
The behavior of a program is undefined if
  • an instantiation of a template specified in [type.traits] directly or indirectly depends on an incompletely-defined object type T, and
  • that instantiation could yield a different result were T hypothetically completed.

21.3.3 Header <type_traits> synopsis [meta.type.synop]

// all freestanding namespace std { // [meta.help], helper class template<class T, T v> struct integral_constant; template<bool B> using bool_constant = integral_constant<bool, B>; using true_type = bool_constant<true>; using false_type = bool_constant<false>; // [meta.unary.cat], primary type categories template<class T> struct is_void; template<class T> struct is_null_pointer; template<class T> struct is_integral; template<class T> struct is_floating_point; template<class T> struct is_array; template<class T> struct is_pointer; template<class T> struct is_lvalue_reference; template<class T> struct is_rvalue_reference; template<class T> struct is_member_object_pointer; template<class T> struct is_member_function_pointer; template<class T> struct is_enum; template<class T> struct is_union; template<class T> struct is_class; template<class T> struct is_function; // [meta.unary.comp], composite type categories template<class T> struct is_reference; template<class T> struct is_arithmetic; template<class T> struct is_fundamental; template<class T> struct is_object; template<class T> struct is_scalar; template<class T> struct is_compound; template<class T> struct is_member_pointer; // [meta.unary.prop], type properties template<class T> struct is_const; template<class T> struct is_volatile; template<class T> struct is_trivial; template<class T> struct is_trivially_copyable; template<class T> struct is_standard_layout; template<class T> struct is_empty; template<class T> struct is_polymorphic; template<class T> struct is_abstract; template<class T> struct is_final; template<class T> struct is_aggregate; template<class T> struct is_signed; template<class T> struct is_unsigned; template<class T> struct is_bounded_array; template<class T> struct is_unbounded_array; template<class T> struct is_scoped_enum; template<class T, class... Args> struct is_constructible; template<class T> struct is_default_constructible; template<class T> struct is_copy_constructible; template<class T> struct is_move_constructible; template<class T, class U> struct is_assignable; template<class T> struct is_copy_assignable; template<class T> struct is_move_assignable; template<class T, class U> struct is_swappable_with; template<class T> struct is_swappable; template<class T> struct is_destructible; template<class T, class... Args> struct is_trivially_constructible; template<class T> struct is_trivially_default_constructible; template<class T> struct is_trivially_copy_constructible; template<class T> struct is_trivially_move_constructible; template<class T, class U> struct is_trivially_assignable; template<class T> struct is_trivially_copy_assignable; template<class T> struct is_trivially_move_assignable; template<class T> struct is_trivially_destructible; template<class T, class... Args> struct is_nothrow_constructible; template<class T> struct is_nothrow_default_constructible; template<class T> struct is_nothrow_copy_constructible; template<class T> struct is_nothrow_move_constructible; template<class T, class U> struct is_nothrow_assignable; template<class T> struct is_nothrow_copy_assignable; template<class T> struct is_nothrow_move_assignable; template<class T, class U> struct is_nothrow_swappable_with; template<class T> struct is_nothrow_swappable; template<class T> struct is_nothrow_destructible; template<class T> struct is_implicit_lifetime; template<class T> struct has_virtual_destructor; template<class T> struct has_unique_object_representations; template<class T, class U> struct reference_constructs_from_temporary; template<class T, class U> struct reference_converts_from_temporary; // [meta.unary.prop.query], type property queries template<class T> struct alignment_of; template<class T> struct rank; template<class T, unsigned I = 0> struct extent; // [meta.rel], type relations template<class T, class U> struct is_same; template<class Base, class Derived> struct is_base_of; template<class Base, class Derived> struct is_virtual_base_of; template<class From, class To> struct is_convertible; template<class From, class To> struct is_nothrow_convertible; template<class T, class U> struct is_layout_compatible; template<class Base, class Derived> struct is_pointer_interconvertible_base_of; template<class Fn, class... ArgTypes> struct is_invocable; template<class R, class Fn, class... ArgTypes> struct is_invocable_r; template<class Fn, class... ArgTypes> struct is_nothrow_invocable; template<class R, class Fn, class... ArgTypes> struct is_nothrow_invocable_r; // [meta.trans.cv], const-volatile modifications template<class T> struct remove_const; template<class T> struct remove_volatile; template<class T> struct remove_cv; template<class T> struct add_const; template<class T> struct add_volatile; template<class T> struct add_cv; template<class T> using remove_const_t = typename remove_const<T>::type; template<class T> using remove_volatile_t = typename remove_volatile<T>::type; template<class T> using remove_cv_t = typename remove_cv<T>::type; template<class T> using add_const_t = typename add_const<T>::type; template<class T> using add_volatile_t = typename add_volatile<T>::type; template<class T> using add_cv_t = typename add_cv<T>::type; // [meta.trans.ref], reference modifications template<class T> struct remove_reference; template<class T> struct add_lvalue_reference; template<class T> struct add_rvalue_reference; template<class T> using remove_reference_t = typename remove_reference<T>::type; template<class T> using add_lvalue_reference_t = typename add_lvalue_reference<T>::type; template<class T> using add_rvalue_reference_t = typename add_rvalue_reference<T>::type; // [meta.trans.sign], sign modifications template<class T> struct make_signed; template<class T> struct make_unsigned; template<class T> using make_signed_t = typename make_signed<T>::type; template<class T> using make_unsigned_t = typename make_unsigned<T>::type; // [meta.trans.arr], array modifications template<class T> struct remove_extent; template<class T> struct remove_all_extents; template<class T> using remove_extent_t = typename remove_extent<T>::type; template<class T> using remove_all_extents_t = typename remove_all_extents<T>::type; // [meta.trans.ptr], pointer modifications template<class T> struct remove_pointer; template<class T> struct add_pointer; template<class T> using remove_pointer_t = typename remove_pointer<T>::type; template<class T> using add_pointer_t = typename add_pointer<T>::type; // [meta.trans.other], other transformations template<class T> struct type_identity; template<class T> struct remove_cvref; template<class T> struct decay; template<bool, class T = void> struct enable_if; template<bool, class T, class F> struct conditional; template<class... T> struct common_type; template<class T, class U, template<class> class TQual, template<class> class UQual> struct basic_common_reference { }; template<class... T> struct common_reference; template<class T> struct underlying_type; template<class Fn, class... ArgTypes> struct invoke_result; template<class T> struct unwrap_reference; template<class T> struct unwrap_ref_decay; template<class T> using type_identity_t = typename type_identity<T>::type; template<class T> using remove_cvref_t = typename remove_cvref<T>::type; template<class T> using decay_t = typename decay<T>::type; template<bool B, class T = void> using enable_if_t = typename enable_if<B, T>::type; template<bool B, class T, class F> using conditional_t = typename conditional<B, T, F>::type; template<class... T> using common_type_t = typename common_type<T...>::type; template<class... T> using common_reference_t = typename common_reference<T...>::type; template<class T> using underlying_type_t = typename underlying_type<T>::type; template<class Fn, class... ArgTypes> using invoke_result_t = typename invoke_result<Fn, ArgTypes...>::type; template<class T> using unwrap_reference_t = typename unwrap_reference<T>::type; template<class T> using unwrap_ref_decay_t = typename unwrap_ref_decay<T>::type; template<class...> using void_t = void; // [meta.logical], logical operator traits template<class... B> struct conjunction; template<class... B> struct disjunction; template<class B> struct negation; // [meta.unary.cat], primary type categories template<class T> constexpr bool is_void_v = is_void<T>::value; template<class T> constexpr bool is_null_pointer_v = is_null_pointer<T>::value; template<class T> constexpr bool is_integral_v = is_integral<T>::value; template<class T> constexpr bool is_floating_point_v = is_floating_point<T>::value; template<class T> constexpr bool is_array_v = is_array<T>::value; template<class T> constexpr bool is_pointer_v = is_pointer<T>::value; template<class T> constexpr bool is_lvalue_reference_v = is_lvalue_reference<T>::value; template<class T> constexpr bool is_rvalue_reference_v = is_rvalue_reference<T>::value; template<class T> constexpr bool is_member_object_pointer_v = is_member_object_pointer<T>::value; template<class T> constexpr bool is_member_function_pointer_v = is_member_function_pointer<T>::value; template<class T> constexpr bool is_enum_v = is_enum<T>::value; template<class T> constexpr bool is_union_v = is_union<T>::value; template<class T> constexpr bool is_class_v = is_class<T>::value; template<class T> constexpr bool is_function_v = is_function<T>::value; // [meta.unary.comp], composite type categories template<class T> constexpr bool is_reference_v = is_reference<T>::value; template<class T> constexpr bool is_arithmetic_v = is_arithmetic<T>::value; template<class T> constexpr bool is_fundamental_v = is_fundamental<T>::value; template<class T> constexpr bool is_object_v = is_object<T>::value; template<class T> constexpr bool is_scalar_v = is_scalar<T>::value; template<class T> constexpr bool is_compound_v = is_compound<T>::value; template<class T> constexpr bool is_member_pointer_v = is_member_pointer<T>::value; // [meta.unary.prop], type properties template<class T> constexpr bool is_const_v = is_const<T>::value; template<class T> constexpr bool is_volatile_v = is_volatile<T>::value; template<class T> constexpr bool is_trivial_v = is_trivial<T>::value; template<class T> constexpr bool is_trivially_copyable_v = is_trivially_copyable<T>::value; template<class T> constexpr bool is_standard_layout_v = is_standard_layout<T>::value; template<class T> constexpr bool is_empty_v = is_empty<T>::value; template<class T> constexpr bool is_polymorphic_v = is_polymorphic<T>::value; template<class T> constexpr bool is_abstract_v = is_abstract<T>::value; template<class T> constexpr bool is_final_v = is_final<T>::value; template<class T> constexpr bool is_aggregate_v = is_aggregate<T>::value; template<class T> constexpr bool is_signed_v = is_signed<T>::value; template<class T> constexpr bool is_unsigned_v = is_unsigned<T>::value; template<class T> constexpr bool is_bounded_array_v = is_bounded_array<T>::value; template<class T> constexpr bool is_unbounded_array_v = is_unbounded_array<T>::value; template<class T> constexpr bool is_scoped_enum_v = is_scoped_enum<T>::value; template<class T, class... Args> constexpr bool is_constructible_v = is_constructible<T, Args...>::value; template<class T> constexpr bool is_default_constructible_v = is_default_constructible<T>::value; template<class T> constexpr bool is_copy_constructible_v = is_copy_constructible<T>::value; template<class T> constexpr bool is_move_constructible_v = is_move_constructible<T>::value; template<class T, class U> constexpr bool is_assignable_v = is_assignable<T, U>::value; template<class T> constexpr bool is_copy_assignable_v = is_copy_assignable<T>::value; template<class T> constexpr bool is_move_assignable_v = is_move_assignable<T>::value; template<class T, class U> constexpr bool is_swappable_with_v = is_swappable_with<T, U>::value; template<class T> constexpr bool is_swappable_v = is_swappable<T>::value; template<class T> constexpr bool is_destructible_v = is_destructible<T>::value; template<class T, class... Args> constexpr bool is_trivially_constructible_v = is_trivially_constructible<T, Args...>::value; template<class T> constexpr bool is_trivially_default_constructible_v = is_trivially_default_constructible<T>::value; template<class T> constexpr bool is_trivially_copy_constructible_v = is_trivially_copy_constructible<T>::value; template<class T> constexpr bool is_trivially_move_constructible_v = is_trivially_move_constructible<T>::value; template<class T, class U> constexpr bool is_trivially_assignable_v = is_trivially_assignable<T, U>::value; template<class T> constexpr bool is_trivially_copy_assignable_v = is_trivially_copy_assignable<T>::value; template<class T> constexpr bool is_trivially_move_assignable_v = is_trivially_move_assignable<T>::value; template<class T> constexpr bool is_trivially_destructible_v = is_trivially_destructible<T>::value; template<class T, class... Args> constexpr bool is_nothrow_constructible_v = is_nothrow_constructible<T, Args...>::value; template<class T> constexpr bool is_nothrow_default_constructible_v = is_nothrow_default_constructible<T>::value; template<class T> constexpr bool is_nothrow_copy_constructible_v = is_nothrow_copy_constructible<T>::value; template<class T> constexpr bool is_nothrow_move_constructible_v = is_nothrow_move_constructible<T>::value; template<class T, class U> constexpr bool is_nothrow_assignable_v = is_nothrow_assignable<T, U>::value; template<class T> constexpr bool is_nothrow_copy_assignable_v = is_nothrow_copy_assignable<T>::value; template<class T> constexpr bool is_nothrow_move_assignable_v = is_nothrow_move_assignable<T>::value; template<class T, class U> constexpr bool is_nothrow_swappable_with_v = is_nothrow_swappable_with<T, U>::value; template<class T> constexpr bool is_nothrow_swappable_v = is_nothrow_swappable<T>::value; template<class T> constexpr bool is_nothrow_destructible_v = is_nothrow_destructible<T>::value; template<class T> constexpr bool is_implicit_lifetime_v = is_implicit_lifetime<T>::value; template<class T> constexpr bool has_virtual_destructor_v = has_virtual_destructor<T>::value; template<class T> constexpr bool has_unique_object_representations_v = has_unique_object_representations<T>::value; template<class T, class U> constexpr bool reference_constructs_from_temporary_v = reference_constructs_from_temporary<T, U>::value; template<class T, class U> constexpr bool reference_converts_from_temporary_v = reference_converts_from_temporary<T, U>::value; // [meta.unary.prop.query], type property queries template<class T> constexpr size_t alignment_of_v = alignment_of<T>::value; template<class T> constexpr size_t rank_v = rank<T>::value; template<class T, unsigned I = 0> constexpr size_t extent_v = extent<T, I>::value; // [meta.rel], type relations template<class T, class U> constexpr bool is_same_v = is_same<T, U>::value; template<class Base, class Derived> constexpr bool is_base_of_v = is_base_of<Base, Derived>::value; template<class Base, class Derived> constexpr bool is_virtual_base_of_v = is_virtual_base_of<Base, Derived>::value; template<class From, class To> constexpr bool is_convertible_v = is_convertible<From, To>::value; template<class From, class To> constexpr bool is_nothrow_convertible_v = is_nothrow_convertible<From, To>::value; template<class T, class U> constexpr bool is_layout_compatible_v = is_layout_compatible<T, U>::value; template<class Base, class Derived> constexpr bool is_pointer_interconvertible_base_of_v = is_pointer_interconvertible_base_of<Base, Derived>::value; template<class Fn, class... ArgTypes> constexpr bool is_invocable_v = is_invocable<Fn, ArgTypes...>::value; template<class R, class Fn, class... ArgTypes> constexpr bool is_invocable_r_v = is_invocable_r<R, Fn, ArgTypes...>::value; template<class Fn, class... ArgTypes> constexpr bool is_nothrow_invocable_v = is_nothrow_invocable<Fn, ArgTypes...>::value; template<class R, class Fn, class... ArgTypes> constexpr bool is_nothrow_invocable_r_v = is_nothrow_invocable_r<R, Fn, ArgTypes...>::value; // [meta.logical], logical operator traits template<class... B> constexpr bool conjunction_v = conjunction<B...>::value; template<class... B> constexpr bool disjunction_v = disjunction<B...>::value; template<class B> constexpr bool negation_v = negation<B>::value; // [meta.member], member relationships template<class S, class M> constexpr bool is_pointer_interconvertible_with_class(M S::*m) noexcept; template<class S1, class S2, class M1, class M2> constexpr bool is_corresponding_member(M1 S1::*m1, M2 S2::*m2) noexcept; // [meta.const.eval], constant evaluation context constexpr bool is_constant_evaluated() noexcept; consteval bool is_within_lifetime(const auto*) noexcept; }

21.3.4 Helper classes [meta.help]

namespace std { template<class T, T v> struct integral_constant { static constexpr T value = v; using value_type = T; using type = integral_constant<T, v>; constexpr operator value_type() const noexcept { return value; } constexpr value_type operator()() const noexcept { return value; } }; }
The class template integral_constant, alias template bool_constant, and its associated typedef-names true_type and false_type are used as base classes to define the interface for various type traits.

21.3.5 Unary type traits [meta.unary]

21.3.5.1 General [meta.unary.general]

Subclause [meta.unary] contains templates that may be used to query the properties of a type at compile time.
Each of these templates shall be a Cpp17UnaryTypeTrait with a base characteristic of true_type if the corresponding condition is true, otherwise false_type.

21.3.5.2 Primary type categories [meta.unary.cat]

The primary type categories specified in Table 49 correspond to the descriptions given in subclause [basic.types] of the C++ standard.
For any given type T, the result of applying one of these templates to T and to cv T shall yield the same result.
[Note 1: 
For any given type T, exactly one of the primary type categories has a value member that evaluates to true.
β€” end note]
Table 49: Primary type category predicates [tab:meta.unary.cat]
Template
Condition
Comments
template<class T>
struct is_void;
T is void
template<class T>
struct is_null_pointer;
T is nullptr_t ([basic.fundamental])
template<class T>
struct is_integral;
T is an integral type ([basic.fundamental])
template<class T>
struct is_floating_point;
T is a floating-point type ([basic.fundamental])
template<class T>
struct is_array;
T is an array type ([basic.compound]) of known or unknown extent
Class template array ([array]) is not an array type.
template<class T>
struct is_pointer;
T is a pointer type ([basic.compound])
Includes pointers to functions but not pointers to non-static members.
template<class T>
struct is_lvalue_reference;
T is an lvalue reference type ([dcl.ref])
template<class T>
struct is_rvalue_reference;
T is an rvalue reference type ([dcl.ref])
template<class T>
struct is_member_object_pointer;
T is a pointer to data member
template<class T>
struct is_member_function_pointer;
T is a pointer to member function
template<class T>
struct is_enum;
T is an enumeration type ([basic.compound])
template<class T>
struct is_union;
T is a union type ([basic.compound])
template<class T>
struct is_class;
T is a non-union class type ([basic.compound])
template<class T>
struct is_function;
T is a function type ([basic.compound])

21.3.5.3 Composite type traits [meta.unary.comp]

The templates specified in Table 50 provide convenient compositions of the primary type categories, corresponding to the descriptions given in subclause [basic.types].
For any given type T, the result of applying one of these templates to T and to cv T shall yield the same result.
Table 50: Composite type category predicates [tab:meta.unary.comp]
Template
Condition
Comments
template<class T>
struct is_reference;
T is an lvalue reference or an rvalue reference
template<class T>
struct is_arithmetic;
T is an arithmetic type ([basic.fundamental])
template<class T>
struct is_fundamental;
T is a fundamental type ([basic.fundamental])
template<class T>
struct is_object;
T is an object type ([basic.types.general])
template<class T>
struct is_scalar;
T is a scalar type ([basic.types.general])
template<class T>
struct is_compound;
T is a compound type ([basic.compound])
template<class T>
struct is_member_pointer;
T is a pointer-to-member type ([basic.compound])

21.3.5.4 Type properties [meta.unary.prop]

The templates specified in Table 51 provide access to some of the more important properties of types.
It is unspecified whether the library defines any full or partial specializations of any of these templates.
For all of the class templates X declared in this subclause, instantiating that template with a template-argument that is a class template specialization may result in the implicit instantiation of the template argument if and only if the semantics of X require that the argument is a complete type.
For the purpose of defining the templates in this subclause, a function call expression declval<T>() for any type T is considered to be a trivial ([basic.types.general], [special]) function call that is not an odr-use ([basic.def.odr]) of declval in the context of the corresponding definition notwithstanding the restrictions of [declval].
For the purpose of defining the templates in this subclause, let VAL<T> for some type T be an expression defined as follows:
  • If T is a reference or function type, VAL<T> is an expression with the same type and value category as declval<T>().
  • Otherwise, VAL<T> is a prvalue that initially has type T.
    [Note 1: 
    If T is cv-qualified, the cv-qualification is subject to adjustment ([expr.type]).
    β€” end note]
Table 51: Type property predicates [tab:meta.unary.prop]
Template
Condition
Preconditions
template<class T>
struct is_const;
T is const-qualified ([basic.type.qualifier])
template<class T>
struct is_volatile;
T is volatile-qualified ([basic.type.qualifier])
template<class T>
struct is_trivial;
T is a trivial type ([basic.types.general])
remove_all_extents_t<T> shall be a complete type or cv void.
template<class T>
struct is_trivially_copyable;
T is a trivially copyable type ([basic.types.general])
remove_all_extents_t<T> shall be a complete type or cv void.
template<class T>
struct is_standard_layout;
T is a standard-layout type ([basic.types.general])
remove_all_extents_t<T> shall be a complete type or cv void.
template<class T>
struct is_empty;
T is a class type, but not a union type, with no non-static data members other than subobjects of zero size, no virtual member functions, no virtual base classes, and no base class B for which is_empty_v<B> is false.
If T is a non-union class type, T shall be a complete type.
template<class T>
struct is_polymorphic;
T is a polymorphic class ([class.virtual])
If T is a non-union class type, T shall be a complete type.
template<class T>
struct is_abstract;
T is an abstract class ([class.abstract])
If T is a non-union class type, T shall be a complete type.
template<class T>
struct is_final;
T is a class type marked with the class-virt-specifier final ([class.pre]).
[Note 2: 
A union is a class type that can be marked with final.
β€” end note]
If T is a class type, T shall be a complete type.
template<class T>
struct is_aggregate;
T is an aggregate type ([dcl.init.aggr])
T shall be an array type, a complete type, or cv void.
template<class T>
struct is_signed;
If is_arithmetic_v<T> is true, the same result as T(-1) < T(0); otherwise, false
template<class T>
struct is_unsigned;
If is_arithmetic_v<T> is true, the same result as T(0) < T(-1); otherwise, false
template<class T>
struct is_bounded_array;
T is an array type of known bound ([dcl.array])
template<class T>
struct is_unbounded_array;
T is an array type of unknown bound ([dcl.array])
template<class T>
struct is_scoped_enum;
T is a scoped enumeration ([dcl.enum])
template<class T, class... Args>
struct is_constructible;
For a function type T or for a cv void type T, is_constructible_v<T, Args...> is false, otherwise see below
T and all types in the template parameter pack Args shall be complete types, cv void, or arrays of unknown bound.
template<class T>
struct is_default_constructible;
is_constructible_v<T> is true.
T shall be a complete type, cv void, or an array of unknown bound.
template<class T>
struct is_copy_constructible;
For a referenceable type T ([defns.referenceable]), the same result as is_constructible_v<T, const T&>, otherwise false.
T shall be a complete type, cv void, or an array of unknown bound.
template<class T>
struct is_move_constructible;
For a referenceable type T, the same result as is_constructible_v<T, T&&>, otherwise false.
T shall be a complete type, cv void, or an array of unknown bound.
template<class T, class U>
struct is_assignable;
The expression declval<T>() = declval<U>() is well-formed when treated as an unevaluated operand ([expr.context]).
Access checking is performed as if in a context unrelated to T and U.
Only the validity of the immediate context of the assignment expression is considered.
[Note 3: 
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]
T and U shall be complete types, cv void, or arrays of unknown bound.
template<class T>
struct is_copy_assignable;
For a referenceable type T, the same result as is_assignable_v<T&, const T&>, otherwise false.
T shall be a complete type, cv void, or an array of unknown bound.
template<class T>
struct is_move_assignable;
For a referenceable type T, the same result as is_assignable_v<T&, T&&>, otherwise false.
T shall be a complete type, cv void, or an array of unknown bound.
template<class T, class U>
struct is_swappable_with;
The expressions swap(declval<T>(), declval<U>()) and swap(declval<U>(), declval<T>()) are each well-formed when treated as an unevaluated operand ([expr.context]) in an overload-resolution context for swappable values ([swappable.requirements]).
Access checking is performed as if in a context unrelated to T and U.
Only the validity of the immediate context of the swap expressions is considered.
[Note 4: 
The compilation of the expressions 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]
T and U shall be complete types, cv void, or arrays of unknown bound.
template<class T>
struct is_swappable;
For a referenceable type T, the same result as is_swappable_with_v<T&, T&>, otherwise false.
T shall be a complete type, cv void, or an array of unknown bound.
template<class T>
struct is_destructible;
Either T is a reference type, or T is a complete object type for which the expression declval<U&>().~U() is well-formed when treated as an unevaluated operand ([expr.context]), where U is remove_all_extents_t<T>.
T shall be a complete type, cv void, or an array of unknown bound.
template<class T, class... Args>
struct
is_trivially_constructible;
is_constructible_v<T,
Args...> is true and the variable definition for is_constructible, as defined below, is known to call no operation that is not trivial ([basic.types.general], [special]).
T and all types in the template parameter pack Args shall be complete types, cv void, or arrays of unknown bound.
template<class T>
struct is_trivially_default_constructible;
is_trivially_constructible_v<T> is true.
T shall be a complete type, cv void, or an array of unknown bound.
template<class T>
struct is_trivially_copy_constructible;
For a referenceable type T, the same result as is_trivially_constructible_v<T, const T&>, otherwise false.
T shall be a complete type, cv void, or an array of unknown bound.
template<class T>
struct is_trivially_move_constructible;
For a referenceable type T, the same result as is_trivially_constructible_v<T, T&&>, otherwise false.
T shall be a complete type, cv void, or an array of unknown bound.
template<class T, class U>
struct is_trivially_assignable;
is_assignable_v<T, U> is true and the assignment, as defined by is_assignable, is known to call no operation that is not trivial ([basic.types.general], [special]).
T and U shall be complete types, cv void, or arrays of unknown bound.
template<class T>
struct is_trivially_copy_assignable;
For a referenceable type T, the same result as is_trivially_assignable_v<T&, const T&>, otherwise false.
T shall be a complete type, cv void, or an array of unknown bound.
template<class T>
struct is_trivially_move_assignable;
For a referenceable type T, the same result as is_trivially_assignable_v<T&, T&&>, otherwise false.
T shall be a complete type, cv void, or an array of unknown bound.
template<class T>
struct is_trivially_destructible;
is_destructible_v<T> is true and remove_all_extents_t<T> is either a non-class type or a class type with a trivial destructor.
T shall be a complete type, cv void, or an array of unknown bound.
template<class T, class... Args>
struct is_nothrow_constructible;
is_constructible_v<T, Args...> is true and the variable definition for is_constructible, as defined below, is known not to throw any exceptions ([expr.unary.noexcept]).
T and all types in the template parameter pack Args shall be complete types, cv void, or arrays of unknown bound.
template<class T>
struct is_nothrow_default_constructible;
is_nothrow_constructible_v<T> is true.
T shall be a complete type, cv void, or an array of unknown bound.
template<class T>
struct is_nothrow_copy_constructible;
For a referenceable type T, the same result as is_nothrow_constructible_v<T, const T&>, otherwise false.
T shall be a complete type, cv void, or an array of unknown bound.
template<class T>
struct is_nothrow_move_constructible;
For a referenceable type T, the same result as is_nothrow_constructible_v<T, T&&>, otherwise false.
T shall be a complete type, cv void, or an array of unknown bound.
template<class T, class U>
struct is_nothrow_assignable;
is_assignable_v<T, U> is true and the assignment is known not to throw any exceptions ([expr.unary.noexcept]).
T and U shall be complete types, cv void, or arrays of unknown bound.
template<class T>
struct is_nothrow_copy_assignable;
For a referenceable type T, the same result as is_nothrow_assignable_v<T&, const T&>, otherwise false.
T shall be a complete type, cv void, or an array of unknown bound.
template<class T>
struct is_nothrow_move_assignable;
For a referenceable type T, the same result as is_nothrow_assignable_v<T&, T&&>, otherwise false.
T shall be a complete type, cv void, or an array of unknown bound.
template<class T, class U>
struct is_nothrow_swappable_with;
is_swappable_with_v<T, U> is true and each swap expression of the definition of is_swappable_with<T, U> is known not to throw any exceptions ([expr.unary.noexcept]).
T and U shall be complete types, cv void, or arrays of unknown bound.
template<class T>
struct is_nothrow_swappable;
For a referenceable type T, the same result as is_nothrow_swappable_with_v<T&, T&>, otherwise false.
T shall be a complete type, cv void, or an array of unknown bound.
template<class T>
struct is_nothrow_destructible;
is_destructible_v<T> is true and the indicated destructor is known not to throw any exceptions ([expr.unary.noexcept]).
T shall be a complete type, cv void, or an array of unknown bound.
template<class T>
struct is_implicit_lifetime;
T is an implicit-lifetime type ([basic.types.general]).
T shall be an array type, a complete type, or cv void.
template<class T>
struct has_virtual_destructor;
T has a virtual destructor ([class.dtor])
If T is a non-union class type, T shall be a complete type.
template<class T>
struct has_unique_object_representations;
For an array type T, the same result as has_unique_object_representations_v<remove_all_extents_t<T>>, otherwise see below.
T shall be a complete type, cv void, or an array of unknown bound.
template<class T, class U>
struct reference_constructs_from_temporary;
T is a reference type, and the initialization T t(VAL<U>); is well-formed and binds t to a temporary object whose lifetime is extended ([class.temporary]).
Access checking is performed as if in a context unrelated to T and U.
Only the validity of the immediate context of the variable initialization is considered.
[Note 5: 
The initialization can result in effects such as the instantiation of class template specializations and function template specializations, the generation of implicitly-defined functions, and so on.
Such effects are not in the β€œimmediate context” and can result in the program being ill-formed.
β€” end note]
T and U shall be complete types, cv void, or arrays of unknown bound.
template<class T, class U>
struct reference_converts_from_temporary;
T is a reference type, and the initialization T t = VAL<U>; is well-formed and binds t to a temporary object whose lifetime is extended ([class.temporary]).
Access checking is performed as if in a context unrelated to T and U.
Only the validity of the immediate context of the variable initialization is considered.
[Note 6: 
The initialization can result in effects such as the instantiation of class template specializations and function template specializations, the generation of implicitly-defined functions, and so on.
Such effects are not in the β€œimmediate context” and can result in the program being ill-formed.
β€” end note]
T and U shall be complete types, cv void, or arrays of unknown bound.
[Example 1: is_const_v<const volatile int> // true is_const_v<const int*> // false is_const_v<const int&> // false is_const_v<int[3]> // false is_const_v<const int[3]> // true β€” end example]
[Example 2: remove_const_t<const volatile int> // volatile int remove_const_t<const int* const> // const int* remove_const_t<const int&> // const int& remove_const_t<const int[3]> // int[3] β€” end example]
[Example 3: // Given: struct P final { }; union U1 { }; union U2 final { }; // the following assertions hold: static_assert(!is_final_v<int>); static_assert(is_final_v<P>); static_assert(!is_final_v<U1>); static_assert(is_final_v<U2>); β€” end example]
The predicate condition for a template specialization is_constructible<T, Args...> shall be satisfied if and only if the following variable definition would be well-formed for some invented variable t:
T t(declval<Args>()...);
[Note 7: 
These tokens are never interpreted as a function declaration.
β€” end note]
Access checking is performed as if in a context unrelated to T and any of the Args.
Only the validity of the immediate context of the variable initialization is considered.
[Note 8: 
The evaluation of the initialization 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]
The predicate condition for a template specialization has_unique_object_representations<T> shall be satisfied if and only if
  • T is trivially copyable, and
  • any two objects of type T with the same value have the same object representation, where
    • two objects of array or non-union class type are considered to have the same value if their respective sequences of direct subobjects have the same values, and
    • two objects of union type are considered to have the same value if they have the same active member and the corresponding members have the same value.
The set of scalar types for which this condition holds is implementation-defined.
[Note 9: 
If a type has padding bits, the condition does not hold; otherwise, the condition holds true for integral types.
β€” end note]

21.3.6 Type property queries [meta.unary.prop.query]

The templates specified in Table 52 may be used to query properties of types at compile time.
Table 52: Type property queries [tab:meta.unary.prop.query]
Template
Value
template<class T>
struct alignment_of;
alignof(T).

Mandates: alignof(T) is a valid expression ([expr.alignof])
template<class T>
struct rank;
If T is an array type, an integer value representing the number of dimensions of T; otherwise, 0.
template<class T,
unsigned I = 0>
struct extent;
If T is not an array type, or if it has rank less than or equal to I, or if I is 0 and T has type β€œarray of unknown bound of U”, then 0; otherwise, the bound ([dcl.array]) of the dimension of T, where indexing of I is zero-based
Each of these templates shall be a Cpp17UnaryTypeTrait ([meta.rqmts]) with a base characteristic of integral_constant<size_t, Value>.
[Example 1: // the following assertions hold: static_assert(rank_v<int> == 0); static_assert(rank_v<int[2]> == 1); static_assert(rank_v<int[][4]> == 2); β€” end example]
[Example 2: // the following assertions hold: static_assert(extent_v<int> == 0); static_assert(extent_v<int[2]> == 2); static_assert(extent_v<int[2][4]> == 2); static_assert(extent_v<int[][4]> == 0); static_assert(extent_v<int, 1> == 0); static_assert(extent_v<int[2], 1> == 0); static_assert(extent_v<int[2][4], 1> == 4); static_assert(extent_v<int[][4], 1> == 4); β€” end example]

21.3.7 Relationships between types [meta.rel]

The templates specified in Table 53 may be used to query relationships between types at compile time.
Each of these templates shall be a Cpp17BinaryTypeTrait ([meta.rqmts]) with a base characteristic of true_type if the corresponding condition is true, otherwise false_type.
Table 53: Type relationship predicates [tab:meta.rel]
Template
Condition
Comments
template<class T, class U>
struct is_same;
T and U name the same type with the same cv-qualifications
template<class Base, class Derived>
struct is_base_of;
Base is a base class of Derived ([class.derived]) without regard to cv-qualifiers or Base and Derived are not unions and name the same class type without regard to cv-qualifiers
If Base and Derived are non-union class types and are not (possibly cv-qualified versions of) the same type, Derived shall be a complete type.
[Note 1: 
Base classes that are private, protected, or ambiguous are, nonetheless, base classes.
β€” end note]
template<class Base, class Derived>
struct is_virtual_base_of;
Base is a virtual base class of Derived ([class.mi]) without regard to cv-qualifiers.
If Base and Derived are non-union class types, Derived shall be a complete type.
[Note 2: 
Virtual base classes that are private, protected, or ambiguous are, nonetheless, virtual base classes.
β€” end note]
[Note 3: 
A class is never a virtual base class of itself.
β€” end note]
template<class From, class To>
struct is_convertible;
see below
From and To shall be complete types, cv void, or arrays of unknown bound.
template<class From, class To>
struct is_nothrow_convertible;
is_convertible_v<From, To> is true and the conversion, as defined by is_convertible, is known not to throw any exceptions ([expr.unary.noexcept])
From and To shall be complete types, cv void, or arrays of unknown bound.
template<class T, class U>
struct is_layout_compatible;
T and U are layout-compatible ([basic.types.general])
T and U shall be complete types, cv void, or arrays of unknown bound.
template<class Base, class Derived>
struct is_pointer_interconvertible_base_of;
Derived is unambiguously derived from Base without regard to cv-qualifiers, and each object of type Derived is pointer-interconvertible ([basic.compound]) with its Base subobject, or Base and Derived are not unions and name the same class type without regard to cv-qualifiers.
If Base and Derived are non-union class types and are not (possibly cv-qualified versions of) the same type, Derived shall be a complete type.
template<class Fn, class... ArgTypes>
struct is_invocable;
The expression INVOKE(declval<Fn>(), declval<ArgTypes>()...) ([func.require]) is well-formed when treated as an unevaluated operand ([expr.context])
Fn and all types in the template parameter pack ArgTypes shall be complete types, cv void, or arrays of unknown bound.
template<class R, class Fn, class... ArgTypes>
struct is_invocable_r;
The expression INVOKE<R>(declval<Fn>(), declval<ArgTypes>()...) is well-formed when treated as an unevaluated operand
Fn, R, and all types in the template parameter pack ArgTypes shall be complete types, cv void, or arrays of unknown bound.
template<class Fn, class... ArgTypes>
struct is_nothrow_invocable;
is_invocable_v<
Fn, ArgTypes...> is true and the expression INVOKE(declval<Fn>(), declval<ArgTypes>()...) is known not to throw any exceptions ([expr.unary.noexcept])
Fn and all types in the template parameter pack ArgTypes shall be complete types, cv void, or arrays of unknown bound.
template<class R, class Fn, class... ArgTypes>
struct is_nothrow_invocable_r;
is_invocable_r_v<
R, Fn, ArgTypes...> is true and the expression INVOKE<R>(declval<Fn>(), declval<ArgTypes>()...) is known not to throw any exceptions ([expr.unary.noexcept])
Fn, R, and all types in the template parameter pack ArgTypes shall be complete types, cv void, or arrays of unknown bound.
For the purpose of defining the templates in this subclause, a function call expression declval<T>() for any type T is considered to be a trivial ([basic.types.general], [special]) function call that is not an odr-use ([basic.def.odr]) of declval in the context of the corresponding definition notwithstanding the restrictions of [declval].
[Example 1: struct B {}; struct B1 : B {}; struct B2 : B {}; struct D : private B1, private B2 {}; is_base_of_v<B, D> // true is_base_of_v<const B, D> // true is_base_of_v<B, const D> // true is_base_of_v<B, const B> // true is_base_of_v<D, B> // false is_base_of_v<B&, D&> // false is_base_of_v<B[3], D[3]> // false is_base_of_v<int, int> // false β€” end example]
The predicate condition for a template specialization is_convertible<From, To> shall be satisfied if and only if the return expression in the following code would be well-formed, including any implicit conversions to the return type of the function: To test() { return declval<From>(); }
[Note 4: 
This requirement gives well-defined results for reference types, array types, function types, and cv void.
β€” end note]
Access checking is performed in a context unrelated to To and From.
Only the validity of the immediate context of the expression of the return statement ([stmt.return]) (including initialization of the returned object or reference) is considered.
[Note 5: 
The initialization 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]

21.3.8 Transformations between types [meta.trans]

21.3.8.1 General [meta.trans.general]

Subclause [meta.trans] contains templates that may be used to transform one type to another following some predefined rule.
Each of the templates in [meta.trans] shall be a Cpp17TransformationTrait ([meta.rqmts]).

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

The templates specified in Table 54 add or remove cv-qualifications ([basic.type.qualifier]).
Table 54: Const-volatile modifications [tab:meta.trans.cv]
Template
Comments
template<class T>
struct remove_const;
The member typedef type denotes the type formed by removing any top-level const-qualifier from T.
[Example 1: 
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 denotes the type formed by removing any top-level volatile-qualifier from T.
[Example 2: 
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 denotes the type formed by removing any top-level cv-qualifiers from T.
[Example 3: 
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 denotes T, otherwise T const.
template<class T>
struct add_volatile;
If T is a reference, function, or top-level volatile-qualified type, then type denotes T, otherwise T volatile.
template<class T>
struct add_cv;
The member typedef type denotes add_const_t<add_volatile_t<T>>.

21.3.8.3 Reference modifications [meta.trans.ref]

The templates specified in Table 55 add or remove references.
Table 55: Reference modifications [tab:meta.trans.ref]
Template
Comments
template<class T>
struct remove_reference;
If T has type β€œreference to T1” then the member typedef type denotes T1; otherwise, type denotes T.
template<class T>
struct add_lvalue_reference;
If T is a referenceable type ([defns.referenceable]) then the member typedef type denotes T&; otherwise, type denotes T.
[Note 1: 
This rule reflects the semantics of reference collapsing ([dcl.ref]).
β€” end note]
template<class T>
struct add_rvalue_reference;
If T is a referenceable type then the member typedef type denotes T&&; otherwise, type denotes T.
[Note 2: 
This rule reflects the semantics of reference collapsing ([dcl.ref]).
For example, when a type T is a reference type T1&, the type add_rvalue_reference_t<T> is not an rvalue reference.
β€” end note]

21.3.8.4 Sign modifications [meta.trans.sign]

The templates specified in Table 56 convert an integer type to its corresponding signed or unsigned type.
Table 56: Sign modifications [tab:meta.trans.sign]
Template
Comments
template<class T>
struct make_signed;
If T is a (possibly cv-qualified) signed integer type ([basic.fundamental]) then the member typedef type denotes T; otherwise, if T is a (possibly cv-qualified) unsigned integer type then type denotes the corresponding signed integer type, with the same cv-qualifiers as T; otherwise, type denotes the signed integer type with smallest rank ([conv.rank]) for which sizeof(T) == sizeof(type), with the same cv-qualifiers as T.

Mandates: T is an integral or enumeration type other than cv bool.
template<class T>
struct make_unsigned;
If T is a (possibly cv-qualified) unsigned integer type ([basic.fundamental]) then the member typedef type denotes T; otherwise, if T is a (possibly cv-qualified) signed integer type then type denotes the corresponding unsigned integer type, with the same cv-qualifiers as T; otherwise, type denotes the unsigned integer type with smallest rank ([conv.rank]) for which sizeof(T) == sizeof(type), with the same cv-qualifiers as T.

Mandates: T is an integral or enumeration type other than cv bool.

21.3.8.5 Array modifications [meta.trans.arr]

The templates specified in Table 57 modify array types.
Table 57: Array modifications [tab:meta.trans.arr]
Template
Comments
template<class T>
struct remove_extent;
If T is a type β€œarray of U”, the member typedef type denotes U, otherwise T.
[Note 1: 
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 β€œmultidimensional array of U”, the resulting member typedef type denotes U, otherwise T.
[Example 1: // the following assertions hold: static_assert(is_same_v<remove_extent_t<int>, int>); static_assert(is_same_v<remove_extent_t<int[2]>, int>); static_assert(is_same_v<remove_extent_t<int[2][3]>, int[3]>); static_assert(is_same_v<remove_extent_t<int[][3]>, int[3]>); β€” end example]
[Example 2: // the following assertions hold: static_assert(is_same_v<remove_all_extents_t<int>, int>); static_assert(is_same_v<remove_all_extents_t<int[2]>, int>); static_assert(is_same_v<remove_all_extents_t<int[2][3]>, int>); static_assert(is_same_v<remove_all_extents_t<int[][3]>, int>); β€” end example]

21.3.8.6 Pointer modifications [meta.trans.ptr]

The templates specified in Table 58 add or remove pointers.
Table 58: Pointer modifications [tab:meta.trans.ptr]
Template
Comments
template<class T>
struct remove_pointer;
If T has type β€œ(possibly cv-qualified) pointer to T1” then the member typedef type denotes T1; otherwise, it denotes T.
template<class T>
struct add_pointer;
If T is a referenceable type ([defns.referenceable]) or a cv void type then the member typedef type denotes remove_reference_t<T>*; otherwise, type denotes T.

21.3.8.7 Other transformations [meta.trans.other]

The templates specified in Table 59 perform other modifications of a type.
Table 59: Other transformations [tab:meta.trans.other]
Template
Comments
template<class T>
struct type_identity;
The member typedef type denotes T.
template<class T>
struct remove_cvref;
The member typedef type denotes remove_cv_t<remove_reference_t<T>>.
template<class T>
struct decay;
If is_array_v<U> is true, the member typedef type denotes remove_extent_t<U>*.
If is_function_v<U> is true, the member typedef type denotes add_pointer_t<U>.
Otherwise the member typedef type denotes remove_cv_t<U>.
[Note 1: 
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 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 denotes 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 denotes T.
If B is false, the member typedef type denotes F.
template<class... T> struct common_type;
Unless this trait is specialized, the member type is defined or omitted as specified below.
If it is omitted, there shall be no member type.
Each type in the template parameter pack T shall be complete, cv void, or an array of unknown bound.
template<class, class, template<class> class, template<class> class> struct basic_common_reference;
Unless this trait is specialized, there shall be no member type.
template<class... T> struct common_reference;
The member typedef-name type is defined or omitted as specified below.
Each type in the parameter pack T shall be complete or cv void.
template<class T>
struct underlying_type;
If T is an enumeration type, the member typedef type denotes the underlying type of T ([dcl.enum]); otherwise, there is no member type.

Mandates: T is not an incomplete enumeration type.
template<class Fn,
class... ArgTypes>
struct invoke_result;
If the expression INVOKE(declval<Fn>(), declval<ArgTypes>()...) ([func.require]) is well-formed when treated as an unevaluated operand ([expr.context]), the member typedef type denotes 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 2: 
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]
Preconditions: Fn and all types in the template parameter pack ArgTypes are complete types, cv void, or arrays of unknown bound.
template<class T> struct unwrap_reference;
If T is a specialization reference_wrapper<X> for some type X, the member typedef type of unwrap_reference<T> denotes X&, otherwise type denotes T.
template<class T> unwrap_ref_decay;
The member typedef type of unwrap_ref_decay<T> denotes the type unwrap_reference_t<decay_t<T>>.
In addition to being available via inclusion of the <type_traits> header, the templates unwrap_reference, unwrap_ref_decay, unwrap_reference_t, and unwrap_ref_decay_t are available when the header <functional> ([functional.syn]) is included.
Let:
  • CREF(A) be add_lvalue_reference_t<const remove_reference_t<A>>,
  • XREF(A) denote a unary alias template T such that T<U> denotes the same type as U with the addition of A's cv and reference qualifiers, for a non-reference cv-unqualified type U,
  • COPYCV(FROM, TO) be an alias for type TO with the addition of FROM's top-level cv-qualifiers,
    [Example 1: 
    COPYCV(const int, volatile short) is an alias for const volatile short.
    β€” end example]
  • COND-RES(X, Y) be decltype(false ? declval<X(&)()>()() : declval<Y(&)()>()()).
Given types A and B, let X be remove_reference_t<A>, let Y be remove_reference_t<B>, and let COMMON-​REF(A, B) be:
  • If A and B are both lvalue reference types, COMMON-REF(A, B) is COND-RES(COPYCV(X, Y) &, COPYCV(​Y, X) &) if that type exists and is a reference type.
  • Otherwise, let C be remove_reference_t<COMMON-REF(X&, Y&)>&&.
    If A and B are both rvalue reference types, C is well-formed, and is_convertible_v<A, C> && is_convertible_v<B, C> is true, then COMMON-REF(A, B) is C.
  • Otherwise, let D be COMMON-REF(const X&, Y&).
    If A is an rvalue reference and B is an lvalue reference and D is well-formed and is_convertible_v<A, D> is true, then COMMON-REF(A, B) is D.
  • Otherwise, if A is an lvalue reference and B is an rvalue reference, then COMMON-REF(A, B) is COMMON-REF(B, A).
  • Otherwise, COMMON-REF(A, B) is ill-formed.
If any of the types computed above is ill-formed, then COMMON-REF(A, B) is ill-formed.
For the common_type trait applied to a template 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, if any, as common_type_t<T0, T0>; otherwise there shall be no member type.
  • 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>.
    • [Note 3: 
      None of the following will apply if there is a specialization common_type<D1, D2>.
      β€” end note]
    • Otherwise, if decay_t<decltype(false ? declval<D1>() : declval<D2>())> denotes a valid type, let C denote that type.
    • Otherwise, if COND-RES(CREF(D1), CREF(D2)) denotes a type, let C denote the type decay_t<COND-RES(CREF(D1), CREF(D2))>.
    In either case, the member typedef-name type shall denote the same type, if any, as C.
    Otherwise, there shall be no member type.
  • 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.
Notwithstanding the provisions of [meta.type.synop], and pursuant to [namespace.std], a program may specialize common_type<T1, T2> for 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 4: 
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, the qualified-id common_type<T1, T2>​::​type shall denote a cv-unqualified non-reference type 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.
For the common_reference 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.
  • Otherwise, if sizeof...(T) is one, let T0 denote the sole type in the pack T.
    The member typedef type shall denote the same type as T0.
  • Otherwise, if sizeof...(T) is two, let T1 and T2 denote the two types in the pack T.
    Then
    • Let R be COMMON-REF(T1, T2).
      If T1 and T2 are reference types, R is well-formed, and is_convertible_v<add_pointer_t<T1>, add_pointer_t<R>> && is_convertible_v<add_pointer_t<T2>, add_pointer_t<R>> is true, then the member typedef type denotes R.
    • Otherwise, if basic_common_reference<remove_cvref_t<T1>, remove_cvref_t<T2>, ​XREF(​T1), XREF(T2)>​::​type is well-formed, then the member typedef type denotes that type.
    • Otherwise, if COND-RES(T1, T2) is well-formed, then the member typedef type denotes that type.
    • Otherwise, if common_type_t<T1, T2> is well-formed, then the member typedef type denotes that type.
    • Otherwise, there shall be no member type.
  • Otherwise, if sizeof...(T) is greater than two, let T1, T2, and Rest, respectively, denote the first, second, and (pack of) remaining types comprising T.
    Let C be the type common_reference_t<T1, T2>.
    Then:
    • If there is such a type C, the member typedef type shall denote the same type, if any, as common_reference_t<C, Rest...>.
    • Otherwise, there shall be no member type.
Notwithstanding the provisions of [meta.type.synop], and pursuant to [namespace.std], a program may partially specialize basic_common_reference<T, U, TQual, UQual> for types T and U such that is_same_v<T, decay_t<T>> and is_same_v<U, decay_t<U>> are each true.
[Note 5: 
Such specializations can be used to influence the result of common_reference, and 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, the qualified-id basic_common_reference<T, U, TQual, UQual>​::​type shall denote a type to which each of the types TQual<T> and UQual<U> is convertible.
Moreover, basic_common_reference<T, U, TQual, UQual>​::​type shall denote the same type, if any, as does basic_common_reference<U, T, UQual, TQual>​::​type.
No diagnostic is required for a violation of these rules.
[Example 2: 
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<invoke_result_t<S, int>, short>); static_assert(is_same_v<invoke_result_t<S&, unsigned char, int&>, double>); static_assert(is_same_v<invoke_result_t<PF1>, bool>); static_assert(is_same_v<invoke_result_t<PMF, unique_ptr<S>, int>, void>); static_assert(is_same_v<invoke_result_t<PMD, S>, char&&>); static_assert(is_same_v<invoke_result_t<PMD, const S*>, const char&>);
β€” end example]

21.3.9 Logical operator traits [meta.logical]

This subclause describes type traits for applying logical operators to other type traits.
template<class... B> struct conjunction : see below { };
The class template conjunction forms the logical conjunction of its template type arguments.
For a specialization conjunction<, , >, if there is a template type argument for which bool(​::​value) is false, then instantiating conjunction<, , >​::​value does not require the instantiation of ​::​value for .
[Note 1: 
This is analogous to the short-circuiting behavior of the built-in operator &&.
β€” end note]
Every template type argument for which ​::​value is instantiated shall be usable as a base class and shall have a member value which is convertible to bool, is not hidden, and is unambiguously available in the type.
The specialization conjunction<, , > has a public and unambiguous base that is either
  • the first type in the list true_type, , , for which bool(​::​value) is false, or
  • if there is no such , the last type in the list.
[Note 2: 
This means a specialization of conjunction does not necessarily inherit from either true_type or false_type.
β€” end note]
The member names of the base class, other than conjunction and operator=, shall not be hidden and shall be unambiguously available in conjunction.
template<class... B> struct disjunction : see below { };
The class template disjunction forms the logical disjunction of its template type arguments.
For a specialization disjunction<, , >, if there is a template type argument for which bool(​::​value) is true, then instantiating disjunction<, , >​::​value does not require the instantiation of ​::​value for .
[Note 3: 
This is analogous to the short-circuiting behavior of the built-in operator ||.
β€” end note]
Every template type argument for which ​::​value is instantiated shall be usable as a base class and shall have a member value which is convertible to bool, is not hidden, and is unambiguously available in the type.
The specialization disjunction<, , > has a public and unambiguous base that is either
  • the first type in the list false_type, , , for which bool(​::​value) is true, or
  • if there is no such , the last type in the list.
[Note 4: 
This means a specialization of disjunction does not necessarily inherit from either true_type or false_type.
β€” end note]
The member names of the base class, other than disjunction and operator=, shall not be hidden and shall be unambiguously available in disjunction.
template<class B> struct negation : see below { };
The class template negation forms the logical negation of its template type argument.
The type negation<B> is a Cpp17UnaryTypeTrait with a base characteristic of bool_constant<!bool(B​::​​value)>.

21.3.10 Member relationships [meta.member]

template<class S, class M> constexpr bool is_pointer_interconvertible_with_class(M S::*m) noexcept;
Mandates: S is a complete type.
Returns: true if and only if S is a standard-layout type, M is an object type, m is not null, and each object s of type S is pointer-interconvertible ([basic.compound]) with its subobject s.*m.
template<class S1, class S2, class M1, class M2> constexpr bool is_corresponding_member(M1 S1::*m1, M2 S2::*m2) noexcept;
Mandates: S1 and S2 are complete types.
Returns: true if and only if S1 and S2 are standard-layout struct ([class.prop]) types, M1 and M2 are object types, m1 and m2 are not null, and m1 and m2 point to corresponding members of the common initial sequence ([class.mem]) of S1 and S2.
[Note 1: 
The type of a pointer-to-member expression &C​::​b is not always a pointer to member of C, leading to potentially surprising results when using these functions in conjunction with inheritance.
[Example 1: struct A { int a; }; // a standard-layout class struct B { int b; }; // a standard-layout class struct C: public A, public B { }; // not a standard-layout class static_assert( is_pointer_interconvertible_with_class( &C::b ) ); // Succeeds because, despite its appearance, &C​::​b has type // β€œpointer to member of B of type int''. static_assert( is_pointer_interconvertible_with_class<C>( &C::b ) ); // Forces the use of class C, and fails. static_assert( is_corresponding_member( &C::a, &C::b ) ); // Succeeds because, despite its appearance, &C​::​a and &C​::​b have types // β€œpointer to member of A of type int'' and // β€œpointer to member of B of type int'', respectively. static_assert( is_corresponding_member<C, C>( &C::a, &C::b ) ); // Forces the use of class C, and fails. β€” end example]
β€” end note]

21.3.11 Constant evaluation context [meta.const.eval]

constexpr bool is_constant_evaluated() noexcept;
Effects: Equivalent to: if consteval { return true; } else { return false; }
[Example 1: constexpr void f(unsigned char *p, int n) { if (std::is_constant_evaluated()) { // should not be a constexpr if statement for (int k = 0; k<n; ++k) p[k] = 0; } else { memset(p, 0, n); // not a core constant expression } } β€” end example]
consteval bool is_within_lifetime(const auto* p) noexcept;
Returns: true if p is a pointer to an object that is within its lifetime ([basic.life]); otherwise, false.
Remarks: During the evaluation of an expression E as a core constant expression, a call to this function is ill-formed unless p points to an object that is usable in constant expressions or whose complete object's lifetime began within E.
[Example 2: struct OptBool { union { bool b; char c; }; // note: this assumes common implementation properties for bool and char: // * sizeof(bool) == sizeof(char), and // * the value representations for true and false are distinct // from the value representation for 2 constexpr OptBool() : c(2) { } constexpr OptBool(bool b) : b(b) { } constexpr auto has_value() const -> bool { if consteval { return std::is_within_lifetime(&b); // during constant evaluation, cannot read from c } else { return c != 2; // during runtime, must read from c } } constexpr auto operator*() const -> const bool& { return b; } }; constexpr OptBool disengaged; constexpr OptBool engaged(true); static_assert(!disengaged.has_value()); static_assert(engaged.has_value()); static_assert(*engaged); β€” end example]