23 General utilities library [utilities]

23.15 Metaprogramming and type traits [meta]

This subclause describes components used by C++ programs, particularly in templates, to support the widest possible range of types, optimise 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 this subclause are signal-safe.

23.15.1 Requirements [meta.rqmts]

A UnaryTypeTrait 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 DefaultConstructible, CopyConstructible, 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 UnaryTypeTrait.
A BinaryTypeTrait 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 DefaultConstructible, CopyConstructible, 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 BinaryTypeTrait.
A TransformationTrait 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.

23.15.2 Header <type_­traits> synopsis [meta.type.synop]

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_pod;
  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, 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 has_virtual_destructor;

  template <class T> struct has_unique_object_representations;

  // [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 From, class To> struct is_convertible;

  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 <size_t Len, size_t Align = default-alignment> // see [meta.trans.other]
    struct aligned_storage;
  template <size_t Len, class... Types> struct aligned_union;
  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> struct underlying_type;
  template <class Fn, class... ArgTypes> struct invoke_result;

  template <size_t Len, size_t Align = default-alignment> // see [meta.trans.other]
    using aligned_storage_t = typename aligned_storage<Len, Align>::type;
  template <size_t Len, class... Types>
    using aligned_union_t   = typename aligned_union<Len, Types...>::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 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...>
    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.endian], endian
  enum class endian {
    little = see below,
    big    = see below,
    native = see below
  };

  // [meta.unary.cat], primary type categories
  template <class T>
    inline constexpr bool is_void_v = is_void<T>::value;
  template <class T>
    inline constexpr bool is_null_pointer_v = is_null_pointer<T>::value;
  template <class T>
    inline constexpr bool is_integral_v = is_integral<T>::value;
  template <class T>
    inline constexpr bool is_floating_point_v = is_floating_point<T>::value;
  template <class T>
    inline constexpr bool is_array_v = is_array<T>::value;
  template <class T>
    inline constexpr bool is_pointer_v = is_pointer<T>::value;
  template <class T>
    inline constexpr bool is_lvalue_reference_v = is_lvalue_reference<T>::value;
  template <class T>
    inline constexpr bool is_rvalue_reference_v = is_rvalue_reference<T>::value;
  template <class T>
    inline constexpr bool is_member_object_pointer_v = is_member_object_pointer<T>::value;
  template <class T>
    inline constexpr bool is_member_function_pointer_v = is_member_function_pointer<T>::value;
  template <class T>
    inline constexpr bool is_enum_v = is_enum<T>::value;
  template <class T>
    inline constexpr bool is_union_v = is_union<T>::value;
  template <class T>
    inline constexpr bool is_class_v = is_class<T>::value;
  template <class T>
    inline constexpr bool is_function_v = is_function<T>::value;

  // [meta.unary.comp], composite type categories
  template <class T>
    inline constexpr bool is_reference_v = is_reference<T>::value;
  template <class T>
    inline constexpr bool is_arithmetic_v = is_arithmetic<T>::value;
  template <class T>
    inline constexpr bool is_fundamental_v = is_fundamental<T>::value;
  template <class T>
    inline constexpr bool is_object_v = is_object<T>::value;
  template <class T>
    inline constexpr bool is_scalar_v = is_scalar<T>::value;
  template <class T>
    inline constexpr bool is_compound_v = is_compound<T>::value;
  template <class T>
    inline constexpr bool is_member_pointer_v = is_member_pointer<T>::value;

  // [meta.unary.prop], type properties
  template <class T>
    inline constexpr bool is_const_v = is_const<T>::value;
  template <class T>
    inline constexpr bool is_volatile_v = is_volatile<T>::value;
  template <class T>
    inline constexpr bool is_trivial_v = is_trivial<T>::value;
  template <class T>
    inline constexpr bool is_trivially_copyable_v = is_trivially_copyable<T>::value;
  template <class T>
    inline constexpr bool is_standard_layout_v = is_standard_layout<T>::value;
  template <class T>
    inline constexpr bool is_pod_v = is_pod<T>::value;
  template <class T>
    inline constexpr bool is_empty_v = is_empty<T>::value;
  template <class T>
    inline constexpr bool is_polymorphic_v = is_polymorphic<T>::value;
  template <class T>
    inline constexpr bool is_abstract_v = is_abstract<T>::value;
  template <class T>
    inline constexpr bool is_final_v = is_final<T>::value;
  template <class T>
    inline constexpr bool is_aggregate_v = is_aggregate<T>::value;
  template <class T>
    inline constexpr bool is_signed_v = is_signed<T>::value;
  template <class T>
    inline constexpr bool is_unsigned_v = is_unsigned<T>::value;
  template <class T, class... Args>
    inline constexpr bool is_constructible_v = is_constructible<T, Args...>::value;
  template <class T>
    inline constexpr bool is_default_constructible_v = is_default_constructible<T>::value;
  template <class T>
    inline constexpr bool is_copy_constructible_v = is_copy_constructible<T>::value;
  template <class T>
    inline constexpr bool is_move_constructible_v = is_move_constructible<T>::value;
  template <class T, class U>
    inline constexpr bool is_assignable_v = is_assignable<T, U>::value;
  template <class T>
    inline constexpr bool is_copy_assignable_v = is_copy_assignable<T>::value;
  template <class T>
    inline constexpr bool is_move_assignable_v = is_move_assignable<T>::value;
  template <class T, class U>
    inline constexpr bool is_swappable_with_v = is_swappable_with<T, U>::value;
  template <class T>
    inline constexpr bool is_swappable_v = is_swappable<T>::value;
  template <class T>
    inline constexpr bool is_destructible_v = is_destructible<T>::value;
  template <class T, class... Args>
    inline constexpr bool is_trivially_constructible_v
      = is_trivially_constructible<T, Args...>::value;
  template <class T>
    inline constexpr bool is_trivially_default_constructible_v
      = is_trivially_default_constructible<T>::value;
  template <class T>
    inline constexpr bool is_trivially_copy_constructible_v
      = is_trivially_copy_constructible<T>::value;
  template <class T>
    inline constexpr bool is_trivially_move_constructible_v
      = is_trivially_move_constructible<T>::value;
  template <class T, class U>
    inline constexpr bool is_trivially_assignable_v = is_trivially_assignable<T, U>::value;
  template <class T>
    inline constexpr bool is_trivially_copy_assignable_v
      = is_trivially_copy_assignable<T>::value;
  template <class T>
    inline constexpr bool is_trivially_move_assignable_v
      = is_trivially_move_assignable<T>::value;
  template <class T>
    inline constexpr bool is_trivially_destructible_v = is_trivially_destructible<T>::value;
  template <class T, class... Args>
    inline constexpr bool is_nothrow_constructible_v
      = is_nothrow_constructible<T, Args...>::value;
  template <class T>
    inline constexpr bool is_nothrow_default_constructible_v
      = is_nothrow_default_constructible<T>::value;
  template <class T>
    inline constexpr bool is_nothrow_copy_constructible_v
    = is_nothrow_copy_constructible<T>::value;
  template <class T>
    inline constexpr bool is_nothrow_move_constructible_v
      = is_nothrow_move_constructible<T>::value;
  template <class T, class U>
    inline constexpr bool is_nothrow_assignable_v = is_nothrow_assignable<T, U>::value;
  template <class T>
    inline constexpr bool is_nothrow_copy_assignable_v = is_nothrow_copy_assignable<T>::value;
  template <class T>
    inline constexpr bool is_nothrow_move_assignable_v = is_nothrow_move_assignable<T>::value;
  template <class T, class U>
    inline constexpr bool is_nothrow_swappable_with_v = is_nothrow_swappable_with<T, U>::value;
  template <class T>
    inline constexpr bool is_nothrow_swappable_v = is_nothrow_swappable<T>::value;
  template <class T>
    inline constexpr bool is_nothrow_destructible_v = is_nothrow_destructible<T>::value;
  template <class T>
    inline constexpr bool has_virtual_destructor_v = has_virtual_destructor<T>::value;
  template <class T>
    inline constexpr bool has_unique_object_representations_v
      = has_unique_object_representations<T>::value;

  // [meta.unary.prop.query], type property queries
  template <class T>
    inline constexpr size_t alignment_of_v = alignment_of<T>::value;
  template <class T>
    inline constexpr size_t rank_v = rank<T>::value;
  template <class T, unsigned I = 0>
    inline constexpr size_t extent_v = extent<T, I>::value;

  // [meta.rel], type relations
  template <class T, class U>
    inline constexpr bool is_same_v = is_same<T, U>::value;
  template <class Base, class Derived>
    inline constexpr bool is_base_of_v = is_base_of<Base, Derived>::value;
  template <class From, class To>
    inline constexpr bool is_convertible_v = is_convertible<From, To>::value;
  template <class Fn, class... ArgTypes>
    inline constexpr bool is_invocable_v = is_invocable<Fn, ArgTypes...>::value;
  template <class R, class Fn, class... ArgTypes>
    inline constexpr bool is_invocable_r_v = is_invocable_r<R, Fn, ArgTypes...>::value;
  template <class Fn, class... ArgTypes>
    inline constexpr bool is_nothrow_invocable_v = is_nothrow_invocable<Fn, ArgTypes...>::value;
  template <class R, class Fn, class... ArgTypes>
    inline constexpr bool is_nothrow_invocable_r_v
      = is_nothrow_invocable_r<R, Fn, ArgTypes...>::value;

  // [meta.logical], logical operator traits
  template<class... B>
    inline constexpr bool conjunction_v = conjunction<B...>::value;
  template<class... B>
    inline constexpr bool disjunction_v = disjunction<B...>::value;
  template<class B>
    inline constexpr bool negation_v = negation<B>::value;
}
The behavior of a program that adds specializations for any of the templates defined in this subclause is undefined unless otherwise specified.
Unless otherwise specified, an incomplete type may be used to instantiate a template in this subclause.

23.15.3 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.

23.15.4 Unary type traits [meta.unary]

This subclause contains templates that may be used to query the properties of a type at compile time.
Each of these templates shall be a UnaryTypeTrait with a base characteristic of true_­type if the corresponding condition is true, otherwise false_­type.

23.15.4.1 Primary type categories [meta.unary.cat]

The primary type categories correspond to the descriptions given in section [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
:
For any given type T, exactly one of the primary type categories has a value member that evaluates to true.
end note
]
Table 40 — Primary type category predicates
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;
template <class T>
struct is_­floating_­point;
template <class T>
struct is_­array;
T is an array type ([basic.compound]) of known or unknown extent
Class template array is not an array type.
template <class T>
struct is_­pointer;
Includes pointers to functions but not pointers to non-static members.
template <class T>
struct is_­lvalue_­reference;
template <class T>
struct is_­rvalue_­reference;
template <class T>
struct is_­member_­object_­pointer;
T is a pointer to non-static data member
template <class T>
struct is_­member_­function_­pointer;
T is a pointer to non-static 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])

23.15.4.2 Composite type traits [meta.unary.comp]

These templates provide convenient compositions of the primary type categories, corresponding to the descriptions given in section [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 41 — Composite type category predicates
Template
Condition
Comments
template <class T>
struct is_­reference;
T is an lvalue reference or an rvalue reference
template <class T>
struct is_­arithmetic;
template <class T>
struct is_­fundamental;
template <class T>
struct is_­object;
T is an object type
template <class T>
struct is_­scalar;
template <class T>
struct is_­compound;
template <class T>
struct is_­member_­pointer;
T is a pointer to non-static data member or non-static member function

23.15.4.3 Type properties [meta.unary.prop]

These templates 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], [special]) function call that is not an odr-use of declval in the context of the corresponding definition notwithstanding the restrictions of [declval].
Table 42 — Type property predicates
Template
Condition
Preconditions
template <class T>
struct is_­const;
template <class T>
struct is_­volatile;
template <class T>
struct is_­trivial;
remove_­all_­extents_­t<T> shall be a complete type or cv void.
template <class T>
struct is_­trivially_­copyable;
remove_­all_­extents_­t<T> shall be a complete type or cv void.
template <class T>
struct is_­standard_­layout;
remove_­all_­extents_­t<T> shall be a complete type or cv void.
template <class T>
struct is_­pod;
T is a POD type
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 bit-fields of length 0, 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;
If T is a non-union class type, T shall be a complete type.
template <class T>
struct is_­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]).
[Note
:
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])
remove_­all_­extents_­t<T> shall be 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, 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 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, 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.
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
:
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 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
:
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, 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], [special]).
T and all types in the 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], [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 the indicated destructor is known to be trivial.
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 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 has_­virtual_­destructor;
T has a virtual destructor
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.
[Example
:
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
:
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
:
// 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
:
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
:
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
:
If a type has padding bits, the condition does not hold; otherwise, the condition holds true for unsigned integral types.
end note
]

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

This subclause contains templates that may be used to query properties of types at compile time.
Table 43 — Type property queries
Template
Value
template <class T>
struct alignment_­of;
alignof(T).

Requires: alignof(T) shall be a valid expression ([expr.alignof])
template <class T>
struct rank;
If T names 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 I'th dimension of T, where indexing of I is zero-based
Each of these templates shall be a UnaryTypeTrait with a base characteristic of integral_­constant<size_­t, Value>.
[Example
:
// the following assertions hold:
assert(rank_v<int> == 0);
assert(rank_v<int[2]> == 1);
assert(rank_v<int[][4]> == 2);
end example
]
[Example
:
// the following assertions hold:
assert(extent_v<int> == 0);
assert(extent_v<int[2]> == 2);
assert(extent_v<int[2][4]> == 2);
assert(extent_v<int[][4]> == 0);
assert((extent_v<int, 1>) == 0);
assert((extent_v<int[2], 1>) == 0);
assert((extent_v<int[2][4], 1>) == 4);
assert((extent_v<int[][4], 1>) == 4);
end example
]

23.15.6 Relationships between types [meta.rel]

This subclause contains templates that may be used to query relationships between types at compile time.
Each of these templates shall be a BinaryTypeTrait with a base characteristic of true_­type if the corresponding condition is true, otherwise false_­type.
Table 44 — Type relationship predicates
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
:
Base classes that are private, protected, or ambiguous are, nonetheless, base classes.
end note
]
template <class From, class To>
struct is_­convertible;
see below
From and To shall be complete types, arrays of unknown bound, or cv void types.
template <class Fn, class... ArgTypes>
struct is_­invocable;
The expression INVOKE(declval<Fn>(), declval<ArgTypes>()...) is well-formed when treated as an unevaluated operand
Fn and all types in the 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 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
Fn and all types in the 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
Fn, R, and all types in the 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], [special]) function call that is not an odr-use of declval in the context of the corresponding definition notwithstanding the restrictions of [declval].
[Example
:
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
:
This requirement gives well-defined results for reference types, void types, array types, and function types.
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 (including initialization of the returned object or reference) is considered.
[Note
:
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
]

23.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.

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

Table 45 — Const-volatile modifications
Template
Comments
template <class T>
struct remove_­const;
The member typedef type names 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 names 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 names 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 names the same type as T, otherwise T volatile.
template <class T>
struct add_­cv;
The member typedef type names the same type as add_­const_­t<add_­volatile_­t<T>>.

23.15.7.2 Reference modifications [meta.trans.ref]

Table 46 — Reference modifications
Template
Comments
template <class T>
struct remove_­reference;
If T has type “reference to T1” then the member typedef type names T1; otherwise, type names T.
template <class T>
struct add_­lvalue_­reference;
If T names a referenceable type then the member typedef type names T&; otherwise, type names 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 names T&&; otherwise, type names 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
]

23.15.7.3 Sign modifications [meta.trans.sign]

Table 47 — Sign modifications
Template
Comments
template <class T>
struct make_­signed;
If T names a (possibly cv-qualified) signed integer type then the member typedef type names the type T; otherwise, if T names a (possibly cv-qualified) unsigned integer type then type names the corresponding signed integer type, with the same cv-qualifiers as T; otherwise, type names the signed integer type with smallest 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 then the member typedef type names the type T; otherwise, if T names a (possibly cv-qualified) signed integer type then type names the corresponding unsigned integer type, with the same cv-qualifiers as T; otherwise, type names the unsigned integer type with smallest 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.

23.15.7.4 Array modifications [meta.trans.arr]

Table 48 — Array modifications
Template
Comments
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
]

23.15.7.5 Pointer modifications [meta.trans.ptr]

Table 49 — Pointer modifications
Template
Comments
template <class T>
struct remove_­pointer;
If T has type “(possibly cv-qualified) pointer to T1” then the member typedef type names T1; otherwise, it names T.
template <class T>
struct add_­pointer;
If T names a referenceable type or a cv void type then the member typedef type names the same type as remove_­reference_­t<T>*; otherwise, type names T.

23.15.7.6 Other transformations [meta.trans.other]

Table 50 — Other transformations
Template
Comments
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, array-to-pointer, and function-to-pointer 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, cv void, or an array of unknown bound.
template <class T>
struct underlying_­type;
The member typedef type names the underlying type of T.

Requires: T shall be a complete enumeration type
template <class Fn,
class... ArgTypes>
struct invoke_­result;
If the expression INVOKE(declval<Fn>(), declval<ArgTypes>()...) is well-formed when treated as an unevaluated operand, the member typedef type names 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, cv 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.
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, 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>.
    • 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.
  • 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 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<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
]

23.15.8 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<B1, ..., BN>, if there is a template type argument Bi for which bool(Bi​::​value) is false, then instantiating conjunction<B1, ..., BN>​::​value does not require the instantiation of Bj​::​value for j > i.
[Note
:
This is analogous to the short-circuiting behavior of the built-in operator &&.
end note
]
Every template type argument for which Bi​::​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<B1, ..., BN> has a public and unambiguous base that is either
  • the first type Bi in the list true_­type, B1, ..., BN for which bool(Bi​::​value) is false, or
  • if there is no such Bi, the last type in the list.
[Note
:
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<B1, ..., BN>, if there is a template type argument Bi for which bool(Bi​::​value) is true, then instantiating disjunction<B1, ..., BN>​::​value does not require the instantiation of Bj​::​value for j > i.
[Note
:
This is analogous to the short-circuiting behavior of the built-in operator ||.
end note
]
Every template type argument for which Bi​::​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<B1, ..., BN> has a public and unambiguous base that is either
  • the first type Bi in the list false_­type, B1, ..., BN for which bool(Bi​::​value) is true, or
  • if there is no such Bi, the last type in the list.
[Note
:
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 UnaryTypeTrait with a base characteristic of bool_­constant<!bool(B​::​value)>.

23.15.9 Endian [meta.endian]

Two common methods of byte ordering in multibyte scalar types are big-endian and little-endian in the execution environment.
Big-endian is a format for storage of binary data in which the most significant byte is placed first, with the rest in descending order.
Little-endian is a format for storage of binary data in which the least significant byte is placed first, with the rest in ascending order.
This subclause describes the endianness of the scalar types of the execution environment.
enum class endian { little = see below, big = see below, native = see below };
If all scalar types have size 1, then all of endian​::​little, endian​::​big, and endian​::​native have the same value.
Otherwise, endian​::​little is not equal to endian​::​big.
If all scalar types are big-endian, endian​::​native is equal to endian​::​big.
If all scalar types are little-endian, endian​::​native is equal to endian​::​little.
Otherwise, endian​::​native is not equal to either endian​::​big or endian​::​little.