[type.traits]

# 21 Metaprogramming library [[meta]](./#meta)

## 21.3 Metaprogramming and type traits [type.traits]

### [21.3.1](#general) General [[type.traits.general]](type.traits.general)

[1](#general-1)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L76)

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[.](#general-1.sentence-1)

It includes type
classification traits, type property inspection traits, and type
transformations[.](#general-1.sentence-2)

The type classification traits describe a complete taxonomy
of all possible C++ types, and state where in that taxonomy a given
type belongs[.](#general-1.sentence-3)

The type property inspection traits allow important
characteristics of types or of combinations of types to be inspected[.](#general-1.sentence-4)

The
type transformations allow certain properties of types to be manipulated[.](#general-1.sentence-5)

[2](#general-2)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L88)

All functions specified in [type.traits] are signal-safe ([[support.signal]](support.signal "17.14.5 Signal handlers"))[.](#general-2.sentence-1)

### [21.3.2](#meta.rqmts) Requirements [[meta.rqmts]](meta.rqmts)

[1](#meta.rqmts-1)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L94)

A *Cpp17UnaryTypeTrait* describes a property
of a type[.](#meta.rqmts-1.sentence-1)

It shall be a class template that takes one template type
argument and, optionally, additional arguments that help define the
property being described[.](#meta.rqmts-1.sentence-2)

It shall be [*Cpp17DefaultConstructible*](utility.arg.requirements#:Cpp17DefaultConstructible "16.4.4.2 Template argument requirements [utility.arg.requirements]"),[*Cpp17CopyConstructible*](utility.arg.requirements#:Cpp17CopyConstructible "16.4.4.2 Template argument requirements [utility.arg.requirements]"),
and publicly and unambiguously derived, directly or indirectly, from
its [*base characteristic*](#def:base_characteristic "21.3.2 Requirements [meta.rqmts]"), which is
a specialization of the template[integral_constant](#lib:integral_constant "21.3.4 Helper classes [meta.help]"), with
the arguments to the template integral_constant determined by the
requirements for the particular property being described[.](#meta.rqmts-1.sentence-3)

The member names of the base characteristic shall not be hidden and shall be
unambiguously available in the *Cpp17UnaryTypeTrait*[.](#meta.rqmts-1.sentence-4)

[2](#meta.rqmts-2)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L109)

A *Cpp17BinaryTypeTrait* describes a
relationship between two types[.](#meta.rqmts-2.sentence-1)

It shall be a class template that
takes two template type arguments and, optionally, additional
arguments that help define the relationship being described[.](#meta.rqmts-2.sentence-2)

It shall
be [*Cpp17DefaultConstructible*](utility.arg.requirements#:Cpp17DefaultConstructible "16.4.4.2 Template argument requirements [utility.arg.requirements]"), [*Cpp17CopyConstructible*](utility.arg.requirements#:Cpp17CopyConstructible "16.4.4.2 Template argument requirements [utility.arg.requirements]"),
and publicly and unambiguously derived, directly or
indirectly, from
its [*base characteristic*](#def:base_characteristic), which is a specialization
of the template[integral_constant](#lib:integral_constant "21.3.4 Helper classes [meta.help]"), with
the arguments to the template integral_constant determined by the
requirements for the particular relationship being described[.](#meta.rqmts-2.sentence-3)

The member names of the base characteristic shall not be hidden and shall be
unambiguously available in the *Cpp17BinaryTypeTrait*[.](#meta.rqmts-2.sentence-4)

[3](#meta.rqmts-3)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L125)

A *Cpp17TransformationTrait* modifies a property
of a type[.](#meta.rqmts-3.sentence-1)

It shall be a class template that takes one
template type argument and, optionally, additional arguments that help
define the modification[.](#meta.rqmts-3.sentence-2)

It shall define a publicly accessible nested type
named type, which shall be a synonym for the modified type[.](#meta.rqmts-3.sentence-3)

[4](#meta.rqmts-4)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L133)

Unless otherwise specified,
the behavior of a program that adds specializations
for any of the templates specified in [type.traits]
is undefined[.](#meta.rqmts-4.sentence-1)

[5](#meta.rqmts-5)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L139)

Unless otherwise specified, an incomplete type may be used
to instantiate a template specified in [type.traits][.](#meta.rqmts-5.sentence-1)

The behavior of a program is undefined if

- [(5.1)](#meta.rqmts-5.1)

an instantiation of a template specified in [type.traits]
 directly or indirectly depends on
 an incompletely-defined object type T, and

- [(5.2)](#meta.rqmts-5.2)

that instantiation could yield a different result
 were T hypothetically completed[.](#meta.rqmts-5.sentence-2)

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

// all freestandingnamespace std {// [[meta.help]](#meta.help "21.3.4 Helper classes"), helper classtemplate<class T, T v> struct integral_constant; template<bool B>using [bool_constant](#lib:bool_constant "21.3.4 Helper classes [meta.help]") = integral_constant<bool, B>; using [true_type](#lib:true_type "21.3.4 Helper classes [meta.help]") = bool_constant<true>; using [false_type](#lib:false_type "21.3.4 Helper classes [meta.help]") = bool_constant<false>; // [[const.wrap.class]](#const.wrap.class "21.3.5 Class template constant_­wrapper"), class template constant_wrappertemplate<class T>struct *cw-fixed-value*; // *exposition only*template<*cw-fixed-value* X, class = typename decltype(*cw-fixed-value*(X))::*type*>struct constant_wrapper; template<class T>concept [*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = // *exposition only*requires { typename constant_wrapper<T::value>; }; struct *cw-operators*; // *exposition only*template<*cw-fixed-value* X>constexpr auto [cw](#lib:cw "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = constant_wrapper<X>{}; // [[meta.unary.cat]](#meta.unary.cat "21.3.6.2 Primary type categories"), primary type categoriestemplate<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; template<class T> struct is_reflection; // [[meta.unary.comp]](#meta.unary.comp "21.3.6.3 Composite type traits"), composite type categoriestemplate<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]](#meta.unary.prop "21.3.6.4 Type properties"), type propertiestemplate<class T> struct is_const; template<class T> struct is_volatile; template<class T> struct is_trivially_copyable; template<class T> struct is_trivially_relocatable; template<class T> struct is_replaceable; 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_consteval_only; 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_nothrow_relocatable; 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]](#meta.unary.prop.query "21.3.7 Type property queries"), type property queriestemplate<class T> struct alignment_of; template<class T> struct rank; template<class T, unsigned I = 0> struct extent; // [[meta.rel]](#meta.rel "21.3.8 Relationships between types"), type relationstemplate<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; template<class Fn, class Tuple> struct is_applicable; template<class Fn, class Tuple> struct is_nothrow_applicable; // [[meta.trans.cv]](#meta.trans.cv "21.3.9.2 Const-volatile modifications"), const-volatile modificationstemplate<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](#lib:remove_const_t "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = typename [remove_const](meta.reflection.traits#lib:remove_const "21.4.17 Reflection type traits [meta.reflection.traits]")<T>::type; template<class T>using [remove_volatile_t](#lib:remove_volatile_t "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = typename [remove_volatile](meta.reflection.traits#lib:remove_volatile "21.4.17 Reflection type traits [meta.reflection.traits]")<T>::type; template<class T>using [remove_cv_t](#lib:remove_cv_t "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = typename [remove_cv](meta.reflection.traits#lib:remove_cv "21.4.17 Reflection type traits [meta.reflection.traits]")<T>::type; template<class T>using [add_const_t](#lib:add_const_t "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = typename [add_const](meta.reflection.traits#lib:add_const "21.4.17 Reflection type traits [meta.reflection.traits]")<T>::type; template<class T>using [add_volatile_t](#lib:add_volatile_t "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = typename [add_volatile](meta.reflection.traits#lib:add_volatile "21.4.17 Reflection type traits [meta.reflection.traits]")<T>::type; template<class T>using [add_cv_t](#lib:add_cv_t "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = typename [add_cv](meta.reflection.traits#lib:add_cv "21.4.17 Reflection type traits [meta.reflection.traits]")<T>::type; // [[meta.trans.ref]](#meta.trans.ref "21.3.9.3 Reference modifications"), reference modificationstemplate<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](#lib:remove_reference_t "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = typename [remove_reference](meta.reflection.traits#lib:remove_reference "21.4.17 Reflection type traits [meta.reflection.traits]")<T>::type; template<class T>using [add_lvalue_reference_t](#lib:add_lvalue_reference_t "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = typename [add_lvalue_reference](meta.reflection.traits#lib:add_lvalue_reference "21.4.17 Reflection type traits [meta.reflection.traits]")<T>::type; template<class T>using [add_rvalue_reference_t](#lib:add_rvalue_reference_t "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = typename [add_rvalue_reference](meta.reflection.traits#lib:add_rvalue_reference "21.4.17 Reflection type traits [meta.reflection.traits]")<T>::type; // [[meta.trans.sign]](#meta.trans.sign "21.3.9.4 Sign modifications"), sign modificationstemplate<class T> struct make_signed; template<class T> struct make_unsigned; template<class T>using [make_signed_t](#lib:make_signed_t "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = typename [make_signed](meta.reflection.traits#lib:make_signed "21.4.17 Reflection type traits [meta.reflection.traits]")<T>::type; template<class T>using [make_unsigned_t](#lib:make_unsigned_t "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = typename [make_unsigned](meta.reflection.traits#lib:make_unsigned "21.4.17 Reflection type traits [meta.reflection.traits]")<T>::type; // [[meta.trans.arr]](#meta.trans.arr "21.3.9.5 Array modifications"), array modificationstemplate<class T> struct remove_extent; template<class T> struct remove_all_extents; template<class T>using [remove_extent_t](#lib:remove_extent_t "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = typename [remove_extent](meta.reflection.traits#lib:remove_extent "21.4.17 Reflection type traits [meta.reflection.traits]")<T>::type; template<class T>using [remove_all_extents_t](#lib:remove_all_extents_t "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = typename [remove_all_extents](meta.reflection.traits#lib:remove_all_extents "21.4.17 Reflection type traits [meta.reflection.traits]")<T>::type; // [[meta.trans.ptr]](#meta.trans.ptr "21.3.9.6 Pointer modifications"), pointer modificationstemplate<class T> struct remove_pointer; template<class T> struct add_pointer; template<class T>using [remove_pointer_t](#lib:remove_pointer_t "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = typename [remove_pointer](meta.reflection.traits#lib:remove_pointer "21.4.17 Reflection type traits [meta.reflection.traits]")<T>::type; template<class T>using [add_pointer_t](#lib:add_pointer_t "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = typename [add_pointer](meta.reflection.traits#lib:add_pointer "21.4.17 Reflection type traits [meta.reflection.traits]")<T>::type; // [[meta.trans.other]](#meta.trans.other "21.3.9.7 Other transformations"), other transformationstemplate<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 Fn, class Tuple> struct apply_result; template<class T> struct unwrap_reference; template<class T> struct unwrap_ref_decay; template<class T>using [type_identity_t](#lib:type_identity_t "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = typename [type_identity](#lib:type_identity "21.3.9.7 Other transformations [meta.trans.other]")<T>::type; template<class T>using [remove_cvref_t](#lib:remove_cvref_t "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = typename [remove_cvref](meta.reflection.traits#lib:remove_cvref "21.4.17 Reflection type traits [meta.reflection.traits]")<T>::type; template<class T>using [decay_t](#lib:decay_t "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = typename [decay](meta.reflection.traits#lib:decay "21.4.17 Reflection type traits [meta.reflection.traits]")<T>::type; template<bool B, class T = void>using [enable_if_t](#lib:enable_if_t "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = typename [enable_if](#lib:enable_if "21.3.9.7 Other transformations [meta.trans.other]")<B, T>::type; template<bool B, class T, class F>using [conditional_t](#lib:conditional_t "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = typename [conditional](#lib:conditional "21.3.9.7 Other transformations [meta.trans.other]")<B, T, F>::type; template<class... T>using [common_type_t](#lib:common_type_t "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = typename [common_type](time.traits.specializations#lib:common_type "30.4.3 Specializations of common_­type [time.traits.specializations]")<T...>::type; template<class... T>using [common_reference_t](#lib:common_reference_t "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = typename [common_reference](meta.reflection.traits#lib:common_reference "21.4.17 Reflection type traits [meta.reflection.traits]")<T...>::type; template<class T>using [underlying_type_t](#lib:underlying_type_t "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = typename [underlying_type](meta.reflection.traits#lib:underlying_type "21.4.17 Reflection type traits [meta.reflection.traits]")<T>::type; template<class Fn, class... ArgTypes>using [invoke_result_t](#lib:invoke_result_t "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = typename [invoke_result](meta.reflection.traits#lib:invoke_result "21.4.17 Reflection type traits [meta.reflection.traits]")<Fn, ArgTypes...>::type; template<class Fn, class Tuple>using [apply_result_t](#lib:apply_result_t "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = typename [apply_result](#lib:apply_result "21.3.9.7 Other transformations [meta.trans.other]")<Fn, Tuple>::type; template<class T>using [unwrap_reference_t](#lib:unwrap_reference_t "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = typename [unwrap_reference](meta.reflection.traits#lib:unwrap_reference "21.4.17 Reflection type traits [meta.reflection.traits]")<T>::type; template<class T>using [unwrap_ref_decay_t](functional.syn#lib:unwrap_ref_decay_t "22.10.2 Header <functional> synopsis [functional.syn]") = typename [unwrap_ref_decay](functional.syn#lib:unwrap_ref_decay "22.10.2 Header <functional> synopsis [functional.syn]")<T>::type; template<class...>using [void_t](#lib:void_t "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = void; // [[meta.logical]](#meta.logical "21.3.10 Logical operator traits"), logical operator traitstemplate<class... B> struct conjunction; template<class... B> struct disjunction; template<class B> struct negation; // [[meta.unary.cat]](#meta.unary.cat "21.3.6.2 Primary type categories"), primary type categoriestemplate<class T>constexpr bool [is_void_v](#lib:is_void_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_void](#lib:is_void "21.3.6.2 Primary type categories [meta.unary.cat]")<T>::value; template<class T>constexpr bool [is_null_pointer_v](#lib:is_null_pointer_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_null_pointer](#lib:is_null_pointer "21.3.6.2 Primary type categories [meta.unary.cat]")<T>::value; template<class T>constexpr bool [is_integral_v](#lib:is_integral_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_integral](#lib:is_integral "21.3.6.2 Primary type categories [meta.unary.cat]")<T>::value; template<class T>constexpr bool [is_floating_point_v](#lib:is_floating_point_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_floating_point](#lib:is_floating_point "21.3.6.2 Primary type categories [meta.unary.cat]")<T>::value; template<class T>constexpr bool [is_array_v](#lib:is_array_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_array](#lib:is_array "21.3.6.2 Primary type categories [meta.unary.cat]")<T>::value; template<class T>constexpr bool [is_pointer_v](#lib:is_pointer_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_pointer](#lib:is_pointer "21.3.6.2 Primary type categories [meta.unary.cat]")<T>::value; template<class T>constexpr bool [is_lvalue_reference_v](#lib:is_lvalue_reference_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_lvalue_reference](#lib:is_lvalue_reference "21.3.6.2 Primary type categories [meta.unary.cat]")<T>::value; template<class T>constexpr bool [is_rvalue_reference_v](#lib:is_rvalue_reference_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_rvalue_reference](#lib:is_rvalue_reference "21.3.6.2 Primary type categories [meta.unary.cat]")<T>::value; template<class T>constexpr bool [is_member_object_pointer_v](#lib:is_member_object_pointer_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_member_object_pointer](#lib:is_member_object_pointer "21.3.6.2 Primary type categories [meta.unary.cat]")<T>::value; template<class T>constexpr bool [is_member_function_pointer_v](#lib:is_member_function_pointer_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_member_function_pointer](#lib:is_member_function_pointer "21.3.6.2 Primary type categories [meta.unary.cat]")<T>::value; template<class T>constexpr bool [is_enum_v](#lib:is_enum_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_enum](#lib:is_enum "21.3.6.2 Primary type categories [meta.unary.cat]")<T>::value; template<class T>constexpr bool [is_union_v](#lib:is_union_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_union](#lib:is_union "21.3.6.2 Primary type categories [meta.unary.cat]")<T>::value; template<class T>constexpr bool [is_class_v](#lib:is_class_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_class](#lib:is_class "21.3.6.2 Primary type categories [meta.unary.cat]")<T>::value; template<class T>constexpr bool [is_function_v](#lib:is_function_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_function](meta.reflection.queries#lib:is_function "21.4.7 Reflection queries [meta.reflection.queries]")<T>::value; template<class T>constexpr bool [is_reflection_v](#lib:is_reflection_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_reflection](#lib:is_reflection "21.3.6.2 Primary type categories [meta.unary.cat]")<T>::value; // [[meta.unary.comp]](#meta.unary.comp "21.3.6.3 Composite type traits"), composite type categoriestemplate<class T>constexpr bool [is_reference_v](#lib:is_reference_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_reference](#lib:is_reference "21.3.6.3 Composite type traits [meta.unary.comp]")<T>::value; template<class T>constexpr bool [is_arithmetic_v](#lib:is_arithmetic_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_arithmetic](#lib:is_arithmetic "21.3.6.3 Composite type traits [meta.unary.comp]")<T>::value; template<class T>constexpr bool [is_fundamental_v](#lib:is_fundamental_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_fundamental](#lib:is_fundamental "21.3.6.3 Composite type traits [meta.unary.comp]")<T>::value; template<class T>constexpr bool [is_object_v](#lib:is_object_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_object](meta.reflection.queries#lib:is_object "21.4.7 Reflection queries [meta.reflection.queries]")<T>::value; template<class T>constexpr bool [is_scalar_v](#lib:is_scalar_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_scalar](#lib:is_scalar "21.3.6.3 Composite type traits [meta.unary.comp]")<T>::value; template<class T>constexpr bool [is_compound_v](#lib:is_compound_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_compound](#lib:is_compound "21.3.6.3 Composite type traits [meta.unary.comp]")<T>::value; template<class T>constexpr bool [is_member_pointer_v](#lib:is_member_pointer_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_member_pointer](#lib:is_member_pointer "21.3.6.3 Composite type traits [meta.unary.comp]")<T>::value; // [[meta.unary.prop]](#meta.unary.prop "21.3.6.4 Type properties"), type propertiestemplate<class T>constexpr bool [is_const_v](#lib:is_const_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_const](meta.reflection.queries#lib:is_const "21.4.7 Reflection queries [meta.reflection.queries]")<T>::value; template<class T>constexpr bool [is_volatile_v](#lib:is_volatile_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_volatile](meta.reflection.queries#lib:is_volatile "21.4.7 Reflection queries [meta.reflection.queries]")<T>::value; template<class T>constexpr bool [is_trivially_copyable_v](#lib:is_trivially_copyable_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_trivially_copyable](#lib:is_trivially_copyable "21.3.6.4 Type properties [meta.unary.prop]")<T>::value; template<class T>constexpr bool [is_trivially_relocatable_v](#lib:is_trivially_relocatable_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_trivially_relocatable](#lib:is_trivially_relocatable "21.3.6.4 Type properties [meta.unary.prop]")<T>::value; template<class T>constexpr bool [is_standard_layout_v](#lib:is_standard_layout_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_standard_layout](#lib:is_standard_layout "21.3.6.4 Type properties [meta.unary.prop]")<T>::value; template<class T>constexpr bool [is_empty_v](#lib:is_empty_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = is_empty<T>::value; template<class T>constexpr bool [is_polymorphic_v](#lib:is_polymorphic_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_polymorphic](#lib:is_polymorphic "21.3.6.4 Type properties [meta.unary.prop]")<T>::value; template<class T>constexpr bool [is_abstract_v](#lib:is_abstract_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_abstract](#lib:is_abstract "21.3.6.4 Type properties [meta.unary.prop]")<T>::value; template<class T>constexpr bool [is_final_v](#lib:is_final_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_final](meta.reflection.queries#lib:is_final "21.4.7 Reflection queries [meta.reflection.queries]")<T>::value; template<class T>constexpr bool [is_aggregate_v](#lib:is_aggregate_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_aggregate](#lib:is_aggregate "21.3.6.4 Type properties [meta.unary.prop]")<T>::value; template<class T>constexpr bool [is_consteval_only_v](#lib:is_consteval_only_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_consteval_only](#lib:is_consteval_only "21.3.6.4 Type properties [meta.unary.prop]")<T>::value; template<class T>constexpr bool [is_signed_v](#lib:is_signed_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = is_signed<T>::value; template<class T>constexpr bool [is_unsigned_v](#lib:is_unsigned_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_unsigned](#lib:is_unsigned "21.3.6.4 Type properties [meta.unary.prop]")<T>::value; template<class T>constexpr bool [is_bounded_array_v](#lib:is_bounded_array_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_bounded_array](#lib:is_bounded_array "21.3.6.4 Type properties [meta.unary.prop]")<T>::value; template<class T>constexpr bool [is_unbounded_array_v](#lib:is_unbounded_array_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_unbounded_array](#lib:is_unbounded_array "21.3.6.4 Type properties [meta.unary.prop]")<T>::value; template<class T>constexpr bool [is_scoped_enum_v](#lib:is_scoped_enum_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_scoped_enum](#lib:is_scoped_enum "21.3.6.4 Type properties [meta.unary.prop]")<T>::value; template<class T, class... Args>constexpr bool [is_constructible_v](#lib:is_constructible_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_constructible](#lib:is_constructible "21.3.6.4 Type properties [meta.unary.prop]")<T, Args...>::value; template<class T>constexpr bool [is_default_constructible_v](#lib:is_default_constructible_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_default_constructible](#lib:is_default_constructible "21.3.6.4 Type properties [meta.unary.prop]")<T>::value; template<class T>constexpr bool [is_copy_constructible_v](#lib:is_copy_constructible_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_copy_constructible](#lib:is_copy_constructible "21.3.6.4 Type properties [meta.unary.prop]")<T>::value; template<class T>constexpr bool [is_move_constructible_v](#lib:is_move_constructible_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_move_constructible](#lib:is_move_constructible "21.3.6.4 Type properties [meta.unary.prop]")<T>::value; template<class T, class U>constexpr bool [is_assignable_v](#lib:is_assignable_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_assignable](#lib:is_assignable "21.3.6.4 Type properties [meta.unary.prop]")<T, U>::value; template<class T>constexpr bool [is_copy_assignable_v](#lib:is_copy_assignable_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_copy_assignable](#lib:is_copy_assignable "21.3.6.4 Type properties [meta.unary.prop]")<T>::value; template<class T>constexpr bool [is_move_assignable_v](#lib:is_move_assignable_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_move_assignable](#lib:is_move_assignable "21.3.6.4 Type properties [meta.unary.prop]")<T>::value; template<class T, class U>constexpr bool [is_swappable_with_v](#lib:is_swappable_with_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_swappable_with](#lib:is_swappable_with "21.3.6.4 Type properties [meta.unary.prop]")<T, U>::value; template<class T>constexpr bool [is_swappable_v](#lib:is_swappable_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_swappable](#lib:is_swappable "21.3.6.4 Type properties [meta.unary.prop]")<T>::value; template<class T>constexpr bool [is_destructible_v](#lib:is_destructible_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_destructible](#lib:is_destructible "21.3.6.4 Type properties [meta.unary.prop]")<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](#lib:is_trivially_default_constructible_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]")= [is_trivially_default_constructible](#lib:is_trivially_default_constructible "21.3.6.4 Type properties [meta.unary.prop]")<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](#lib:is_trivially_assignable_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_trivially_assignable](#lib:is_trivially_assignable "21.3.6.4 Type properties [meta.unary.prop]")<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](#lib:is_trivially_destructible_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_trivially_destructible](#lib:is_trivially_destructible "21.3.6.4 Type properties [meta.unary.prop]")<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](#lib:is_nothrow_default_constructible_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]")= [is_nothrow_default_constructible](#lib:is_nothrow_default_constructible "21.3.6.4 Type properties [meta.unary.prop]")<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](#lib:is_nothrow_assignable_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_nothrow_assignable](#lib:is_nothrow_assignable "21.3.6.4 Type properties [meta.unary.prop]")<T, U>::value; template<class T>constexpr bool [is_nothrow_copy_assignable_v](#lib:is_nothrow_copy_assignable_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_nothrow_copy_assignable](#lib:is_nothrow_copy_assignable "21.3.6.4 Type properties [meta.unary.prop]")<T>::value; template<class T>constexpr bool [is_nothrow_move_assignable_v](#lib:is_nothrow_move_assignable_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_nothrow_move_assignable](#lib:is_nothrow_move_assignable "21.3.6.4 Type properties [meta.unary.prop]")<T>::value; template<class T, class U>constexpr bool [is_nothrow_swappable_with_v](#lib:is_nothrow_swappable_with_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_nothrow_swappable_with](#lib:is_nothrow_swappable_with "21.3.6.4 Type properties [meta.unary.prop]")<T, U>::value; template<class T>constexpr bool [is_nothrow_swappable_v](#lib:is_nothrow_swappable_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_nothrow_swappable](#lib:is_nothrow_swappable "21.3.6.4 Type properties [meta.unary.prop]")<T>::value; template<class T>constexpr bool [is_nothrow_destructible_v](#lib:is_nothrow_destructible_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_nothrow_destructible](#lib:is_nothrow_destructible "21.3.6.4 Type properties [meta.unary.prop]")<T>::value; template<class T>constexpr bool [is_nothrow_relocatable_v](#lib:is_nothrow_relocatable_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_nothrow_relocatable](#lib:is_nothrow_relocatable "21.3.6.4 Type properties [meta.unary.prop]")<T>::value; template<class T>constexpr bool [is_implicit_lifetime_v](#lib:is_implicit_lifetime_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_implicit_lifetime](#lib:is_implicit_lifetime "21.3.6.4 Type properties [meta.unary.prop]")<T>::value; template<class T>constexpr bool [is_replaceable_v](#lib:is_replaceable_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_replaceable](#lib:is_replaceable "21.3.6.4 Type properties [meta.unary.prop]")<T>::value; template<class T>constexpr bool [has_virtual_destructor_v](#lib:has_virtual_destructor_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [has_virtual_destructor](meta.reflection.traits#lib:has_virtual_destructor "21.4.17 Reflection type traits [meta.reflection.traits]")<T>::value; template<class T>constexpr bool [has_unique_object_representations_v](#lib:has_unique_object_representations_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]")= [has_unique_object_representations](meta.reflection.traits#lib:has_unique_object_representations "21.4.17 Reflection type traits [meta.reflection.traits]")<T>::value; template<class T, class U>constexpr bool [reference_constructs_from_temporary_v](#lib:reference_constructs_from_temporary_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]")= [reference_constructs_from_temporary](meta.reflection.traits#lib:reference_constructs_from_temporary "21.4.17 Reflection type traits [meta.reflection.traits]")<T, U>::value; template<class T, class U>constexpr bool [reference_converts_from_temporary_v](#lib:reference_converts_from_temporary_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]")= [reference_converts_from_temporary](meta.reflection.traits#lib:reference_converts_from_temporary "21.4.17 Reflection type traits [meta.reflection.traits]")<T, U>::value; // [[meta.unary.prop.query]](#meta.unary.prop.query "21.3.7 Type property queries"), type property queriestemplate<class T>constexpr size_t [alignment_of_v](#lib:alignment_of_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [alignment_of](meta.reflection.layout#lib:alignment_of "21.4.11 Reflection layout queries [meta.reflection.layout]")<T>::value; template<class T>constexpr size_t [rank_v](#lib:rank_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [rank](meta.reflection.traits#lib:rank "21.4.17 Reflection type traits [meta.reflection.traits]")<T>::value; template<class T, unsigned I = 0>constexpr size_t [extent_v](#lib:extent_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [extent](meta.reflection.traits#lib:extent "21.4.17 Reflection type traits [meta.reflection.traits]")<T, I>::value; // [[meta.rel]](#meta.rel "21.3.8 Relationships between types"), type relationstemplate<class T, class U>constexpr bool [is_same_v](#lib:is_same_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_same](#lib:is_same "21.3.8 Relationships between types [meta.rel]")<T, U>::value; template<class Base, class Derived>constexpr bool [is_base_of_v](#lib:is_base_of_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_base_of](#lib:is_base_of "21.3.8 Relationships between types [meta.rel]")<Base, Derived>::value; template<class Base, class Derived>constexpr bool [is_virtual_base_of_v](#lib:is_virtual_base_of_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_virtual_base_of](#lib:is_virtual_base_of "21.3.8 Relationships between types [meta.rel]")<Base, Derived>::value; template<class From, class To>constexpr bool [is_convertible_v](#lib:is_convertible_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_convertible](#lib:is_convertible "21.3.8 Relationships between types [meta.rel]")<From, To>::value; template<class From, class To>constexpr bool [is_nothrow_convertible_v](#lib:is_nothrow_convertible_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_nothrow_convertible](#lib:is_nothrow_convertible "21.3.8 Relationships between types [meta.rel]")<From, To>::value; template<class T, class U>constexpr bool [is_layout_compatible_v](#lib:is_layout_compatible_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_layout_compatible](#lib:is_layout_compatible "21.3.8 Relationships between types [meta.rel]")<T, U>::value; template<class Base, class Derived>constexpr bool [is_pointer_interconvertible_base_of_v](#lib:is_pointer_interconvertible_base_of_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]")= [is_pointer_interconvertible_base_of](#lib:is_pointer_interconvertible_base_of "21.3.8 Relationships between types [meta.rel]")<Base, Derived>::value; template<class Fn, class... ArgTypes>constexpr bool [is_invocable_v](#lib:is_invocable_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_invocable](#lib:is_invocable "21.3.8 Relationships between types [meta.rel]")<Fn, ArgTypes...>::value; template<class R, class Fn, class... ArgTypes>constexpr bool [is_invocable_r_v](#lib:is_invocable_r_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_invocable_r](#lib:is_invocable_r "21.3.8 Relationships between types [meta.rel]")<R, Fn, ArgTypes...>::value; template<class Fn, class... ArgTypes>constexpr bool [is_nothrow_invocable_v](#lib:is_nothrow_invocable_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [is_nothrow_invocable](#lib:is_nothrow_invocable "21.3.8 Relationships between types [meta.rel]")<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; template<class Fn, class Tuple>constexpr bool [is_applicable_v](#lib:is_applicable_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = is_applicable<Fn, Tuple>::value; template<class Fn, class Tuple>constexpr bool [is_nothrow_applicable_v](#lib:is_nothrow_applicable_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = is_nothrow_applicable<Fn, Tuple>::value; // [[meta.logical]](#meta.logical "21.3.10 Logical operator traits"), logical operator traitstemplate<class... B>constexpr bool [conjunction_v](#lib:conjunction_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [conjunction](#lib:conjunction "21.3.10 Logical operator traits [meta.logical]")<B...>::value; template<class... B>constexpr bool [disjunction_v](#lib:disjunction_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [disjunction](#lib:disjunction "21.3.10 Logical operator traits [meta.logical]")<B...>::value; template<class B>constexpr bool [negation_v](#lib:negation_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") = [negation](#lib:negation "21.3.10 Logical operator traits [meta.logical]")<B>::value; // [[meta.member]](#meta.member "21.3.11 Member relationships"), member relationshipstemplate<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]](#meta.const.eval "21.3.12 Constant evaluation context"), constant evaluation contextconstexpr bool is_constant_evaluated() noexcept; consteval bool is_within_lifetime(const auto*) noexcept;}

### [21.3.4](#meta.help) Helper classes [[meta.help]](meta.help)

[ð](#lib:value_type,integral_constant)

namespace std {template<class T, T v> struct [integral_constant](#lib:integral_constant "21.3.4 Helper classes [meta.help]") {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; }};}

[1](#meta.help-1)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L643)

The class template integral_constant,
alias template bool_constant, and
its associated [*typedef-name*](dcl.typedef#nt:typedef-name "9.2.4 The typedef specifier [dcl.typedef]")*s*true_type and false_type are used as base classes to define
the interface for various type traits[.](#meta.help-1.sentence-1)

### [21.3.5](#const.wrap.class) Class template constant_wrapper [[const.wrap.class]](const.wrap.class)

template<class T>struct *cw-fixed-value* { // *exposition only*using *type* = T; // *exposition only*constexpr *cw-fixed-value*(*type* v) noexcept : *data*(v) {} T *data*; // *exposition only*};

template<class T, size_t Extent>struct *cw-fixed-value*<T[Extent]> { // *exposition only*using *type* = T[Extent]; // *exposition only*constexpr *cw-fixed-value*(T (&arr)[Extent]) noexcept;
 T *data*[Extent]; // *exposition only*};

template<class T, size_t Extent>*cw-fixed-value*(T (&)[Extent]) -> *cw-fixed-value*<T[Extent]>; // *exposition only*struct *cw-operators* { // *exposition only*// unary operatorstemplate<[*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") T>friend constexpr auto operator+(T) noexcept -> constant_wrapper<(+T::value)>{ return {}; }template<[*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") T>friend constexpr auto operator-(T) noexcept -> constant_wrapper<(-T::value)>{ return {}; }template<[*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") T>friend constexpr auto operator~(T) noexcept -> constant_wrapper<(~T::value)>{ return {}; }template<[*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") T>friend constexpr auto operator!(T) noexcept -> constant_wrapper<(!T::value)>{ return {}; }template<[*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") T>friend constexpr auto operator&(T) noexcept -> constant_wrapper<(&T::value)>{ return {}; }template<[*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") T>friend constexpr auto operator*(T) noexcept -> constant_wrapper<(*T::value)>{ return {}; }// binary operatorstemplate<[*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") L, [*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") R>friend constexpr auto operator+(L, R) noexcept -> constant_wrapper<(L::value + R::value)>{ return {}; }template<[*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") L, [*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") R>friend constexpr auto operator-(L, R) noexcept -> constant_wrapper<(L::value - R::value)>{ return {}; }template<[*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") L, [*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") R>friend constexpr auto operator*(L, R) noexcept -> constant_wrapper<(L::value * R::value)>{ return {}; }template<[*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") L, [*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") R>friend constexpr auto operator/(L, R) noexcept -> constant_wrapper<(L::value / R::value)>{ return {}; }template<[*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") L, [*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") R>friend constexpr auto operator%(L, R) noexcept -> constant_wrapper<(L::value % R::value)>{ return {}; }template<[*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") L, [*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") R>friend constexpr auto operator<<(L, R) noexcept -> constant_wrapper<(L::value << R::value)>{ return {}; }template<[*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") L, [*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") R>friend constexpr auto operator>>(L, R) noexcept -> constant_wrapper<(L::value >> R::value)>{ return {}; }template<[*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") L, [*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") R>friend constexpr auto operator&(L, R) noexcept -> constant_wrapper<(L::value & R::value)>{ return {}; }template<[*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") L, [*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") R>friend constexpr auto operator|(L, R) noexcept -> constant_wrapper<(L::value | R::value)>{ return {}; }template<[*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") L, [*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") R>friend constexpr auto operator^(L, R) noexcept -> constant_wrapper<(L::value ^ R::value)>{ return {}; }template<[*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") L, [*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") R>requires (!is_constructible_v<bool, decltype(L::value)> ||!is_constructible_v<bool, decltype(R::value)>)friend constexpr auto operator&&(L, R) noexcept -> constant_wrapper<(L::value && R::value)>{ return {}; }template<[*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") L, [*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") R>requires (!is_constructible_v<bool, decltype(L::value)> ||!is_constructible_v<bool, decltype(R::value)>)friend constexpr auto operator||(L, R) noexcept -> constant_wrapper<(L::value || R::value)>{ return {}; }// comparisonstemplate<[*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") L, [*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") R>friend constexpr auto operator<=>(L, R) noexcept -> constant_wrapper<(L::value <=> R::value)>{ return {}; }template<[*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") L, [*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") R>friend constexpr auto operator<(L, R) noexcept -> constant_wrapper<(L::value < R::value)>{ return {}; }template<[*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") L, [*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") R>friend constexpr auto operator<=(L, R) noexcept -> constant_wrapper<(L::value <= R::value)>{ return {}; }template<[*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") L, [*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") R>friend constexpr auto operator==(L, R) noexcept -> constant_wrapper<(L::value == R::value)>{ return {}; }template<[*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") L, [*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") R>friend constexpr auto operator!=(L, R) noexcept -> constant_wrapper<(L::value != R::value)>{ return {}; }template<[*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") L, [*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") R>friend constexpr auto operator>(L, R) noexcept -> constant_wrapper<(L::value > R::value)>{ return {}; }template<[*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") L, [*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") R>friend constexpr auto operator>=(L, R) noexcept -> constant_wrapper<(L::value >= R::value)>{ return {}; }template<[*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") L, [*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") R>friend constexpr auto operator,(L, R) noexcept = delete; template<[*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") L, [*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") R>friend constexpr auto operator->*(L, R) noexcept -> constant_wrapper<L::value->*(R::value)>{ return {}; }// call and indextemplate<[*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") T, [*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]")... Args>constexpr auto operator()(this T, Args...) noexceptrequires requires(Args...) { constant_wrapper<T::value(Args::value...)>(); }{ return constant_wrapper<T::value(Args::value...)>{}; }template<[*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") T, [*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]")... Args>constexpr auto operator[](this T, Args...) noexcept-> constant_wrapper<(T::value[Args::value...])>{ return {}; }// pseudo-mutatorstemplate<[*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") T>constexpr auto operator++(this T) noexceptrequires requires(T::value_type x) { ++x; }{ return constant_wrapper<[] { auto c = T::value; return ++c; }()>{}; }template<[*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") T>constexpr auto operator++(this T, int) noexceptrequires requires(T::value_type x) { x++; }{ return constant_wrapper<[] { auto c = T::value; return c++; }()>{}; }template<[*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") T>constexpr auto operator--(this T) noexceptrequires requires(T::value_type x) { --x; }{ return constant_wrapper<[] { auto c = T::value; return --c; }()>{}; }template<[*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") T>constexpr auto operator--(this T, int) noexceptrequires requires(T::value_type x) { x--; }{ return constant_wrapper<[] { auto c = T::value; return c--; }()>{}; }template<[*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") T, [*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") R>constexpr auto operator+=(this T, R) noexceptrequires requires(T::value_type x) { x += R::value; }{ return constant_wrapper<[] { auto v = T::value; return v += R::value; }()>{}; }template<[*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") T, [*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") R>constexpr auto operator-=(this T, R) noexceptrequires requires(T::value_type x) { x -= R::value; }{ return constant_wrapper<[] { auto v = T::value; return v -= R::value; }()>{}; }template<[*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") T, [*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") R>constexpr auto operator*=(this T, R) noexceptrequires requires(T::value_type x) { x *= R::value; }{ return constant_wrapper<[] { auto v = T::value; return v *= R::value; }()>{}; }template<[*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") T, [*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") R>constexpr auto operator/=(this T, R) noexceptrequires requires(T::value_type x) { x /= R::value; }{ return constant_wrapper<[] { auto v = T::value; return v /= R::value; }()>{}; }template<[*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") T, [*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") R>constexpr auto operator%=(this T, R) noexceptrequires requires(T::value_type x) { x %= R::value; }{ return constant_wrapper<[] { auto v = T::value; return v %= R::value; }()>{}; }template<[*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") T, [*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") R>constexpr auto operator&=(this T, R) noexceptrequires requires(T::value_type x) { x &= R::value; }{ return constant_wrapper<[] { auto v = T::value; return v &= R::value; }()>{}; }template<[*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") T, [*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") R>constexpr auto operator|=(this T, R) noexceptrequires requires(T::value_type x) { x |= R::value; }{ return constant_wrapper<[] { auto v = T::value; return v |= R::value; }()>{}; }template<[*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") T, [*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") R>constexpr auto operator^=(this T, R) noexceptrequires requires(T::value_type x) { x ^= R::value; }{ return constant_wrapper<[] { auto v = T::value; return v ^= R::value; }()>{}; }template<[*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") T, [*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") R>constexpr auto operator<<=(this T, R) noexceptrequires requires(T::value_type x) { x <<= R::value; }{ return constant_wrapper<[] { auto v = T::value; return v <<= R::value; }()>{}; }template<[*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") T, [*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") R>constexpr auto operator>>=(this T, R) noexceptrequires requires(T::value_type x) { x >>= R::value; }{ return constant_wrapper<[] { auto v = T::value; return v >>= R::value; }()>{}; }};

template<*cw-fixed-value* X, class>struct constant_wrapper : cw-operators {static constexpr const auto & value = X.*data*; using type = constant_wrapper; using value_type = typename decltype(X)::*type*; template<[*constexpr-param*](#concept:constexpr-param "21.3.3 Header <type_­traits> synopsis [meta.type.synop]") R>constexpr auto operator=(R) const noexceptrequires requires(value_type x) { x = R::value; }{ return constant_wrapper<[] { auto v = value; return v = R::value; }()>{}; }constexpr operator decltype(auto)() const noexcept { return value; }};

[1](#const.wrap.class-1)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L851)

The class template constant_wrapper aids in metaprogramming by ensuring
that the evaluation of expressions comprised entirely of constant_wrapper are core constant expressions ([[expr.const]](expr.const "7.7 Constant expressions")),
regardless of the context in which they appear[.](#const.wrap.class-1.sentence-1)

In particular, this enables use of constant_wrapper values
that are passed as arguments to constexpr functions to be used in constant expressions[.](#const.wrap.class-1.sentence-2)

[2](#const.wrap.class-2)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L859)

[*Note [1](#const.wrap.class-note-1)*:

The unnamed second template parameter to constant_wrapper is present
to aid argument-dependent lookup ([[basic.lookup.argdep]](basic.lookup.argdep "6.5.4 Argument-dependent name lookup"))
in finding overloads for which constant_wrapper's wrapped value is a suitable argument,
but for which the constant_wrapper itself is not[.](#const.wrap.class-2.sentence-1)

â *end note*]

[3](#const.wrap.class-3)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L867)

[*Example [1](#const.wrap.class-example-1)*:  constexpr auto initial_phase(auto quantity_1, auto quantity_2) {return quantity_1 + quantity_2; }constexpr auto middle_phase(auto tbd) {return tbd; }void final_phase(auto gathered, auto available) {if constexpr (gathered == available) std::cout << "Profit!\n"; }void impeccable_underground_planning() {auto gathered_quantity = middle_phase(initial_phase(std::cw<42>, std::cw<13>)); static_assert(gathered_quantity == 55); auto all_available = std::cw<55>;
 final_phase(gathered_quantity, all_available); }void deeply_flawed_underground_planning() {constexpr auto gathered_quantity = middle_phase(initial_phase(42, 13)); constexpr auto all_available = 55;
 final_phase(gathered_quantity, all_available); // error: gathered == available// is not a constant expression} â *end example*]

[ð](#const.wrap.class-itemdecl:1)

`constexpr cw-fixed-value(T (&arr)[Extent]) noexcept;
`

[4](#const.wrap.class-4)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L904)

*Effects*: Initialize elements of *data* with corresponding elements of arr[.](#const.wrap.class-4.sentence-1)

### [21.3.6](#meta.unary) Unary type traits [[meta.unary]](meta.unary)

#### [21.3.6.1](#meta.unary.general) General [[meta.unary.general]](meta.unary.general)

[1](#meta.unary.general-1)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L913)

Subclause [[meta.unary]](#meta.unary "21.3.6 Unary type traits") contains templates that may be used to query the
properties of a type at compile time[.](#meta.unary.general-1.sentence-1)

[2](#meta.unary.general-2)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L917)

Each of these templates shall be a[*Cpp17UnaryTypeTrait*](#:Cpp17UnaryTypeTrait "21.3.2 Requirements [meta.rqmts]") with a base characteristic oftrue_type if the corresponding condition is true, otherwisefalse_type[.](#meta.unary.general-2.sentence-1)

#### [21.3.6.2](#meta.unary.cat) Primary type categories [[meta.unary.cat]](meta.unary.cat)

[1](#meta.unary.cat-1)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L926)

The primary type categories specified in Table [52](#tab:meta.unary.cat "Table 52: Primary type category predicates") correspond to the descriptions given in
subclause [[basic.types]](basic.types "6.9 Types") of the C++ standard[.](#meta.unary.cat-1.sentence-1)

[2](#meta.unary.cat-2)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L931)

For any given type T, the result of applying one of these templates toT and to cv T shall yield the same result[.](#meta.unary.cat-2.sentence-1)

[3](#meta.unary.cat-3)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L935)

[*Note [1](#meta.unary.cat-note-1)*:

For any given type T, exactly one of the primary type categories
has a value member that evaluates to true[.](#meta.unary.cat-3.sentence-1)

â *end note*]

Table [52](#tab:meta.unary.cat) — Primary type category predicates [[tab:meta.unary.cat]](./tab:meta.unary.cat)  

| [ð](#tab:meta.unary.cat-row-1)<br>**Template** | **Condition** | **Comments** |
| --- | --- | --- |
| [ð](#tab:meta.unary.cat-row-2)<br>template<class T>   struct [is_void](#lib:is_void "21.3.6.2 Primary type categories [meta.unary.cat]"); | T is void |  |
| [ð](#tab:meta.unary.cat-row-3)<br>template<class T>   struct [is_null_pointer](#lib:is_null_pointer "21.3.6.2 Primary type categories [meta.unary.cat]"); | T is nullptr_t ([[basic.fundamental]](basic.fundamental "6.9.2 Fundamental types")) |  |
| [ð](#tab:meta.unary.cat-row-4)<br>template<class T>   struct [is_integral](#lib:is_integral "21.3.6.2 Primary type categories [meta.unary.cat]"); | T is an integral type ([[basic.fundamental]](basic.fundamental "6.9.2 Fundamental types")) |  |
| [ð](#tab:meta.unary.cat-row-5)<br>template<class T>   struct is_floating_point; | T is a floating-point type ([[basic.fundamental]](basic.fundamental "6.9.2 Fundamental types")) |  |
| [ð](#tab:meta.unary.cat-row-6)<br>template<class T>   struct is_array; | T is an array type ([[basic.compound]](basic.compound "6.9.4 Compound types")) of known or unknown extent | Class template array ([[array]](array "23.3.3 Class template array")) is not an array type[.](#tab:meta.unary.cat-row-6-column-3-sentence-1) |
| [ð](#tab:meta.unary.cat-row-7)<br>template<class T>   struct is_pointer; | T is a pointer type ([[basic.compound]](basic.compound "6.9.4 Compound types")) | Includes pointers to functions but not pointers to non-static members[.](#tab:meta.unary.cat-row-7-column-3-sentence-1) |
| [ð](#tab:meta.unary.cat-row-8)<br>template<class T>   struct is_lvalue_reference; | T is an lvalue reference type ([[dcl.ref]](dcl.ref "9.3.4.3 References")) |  |
| [ð](#tab:meta.unary.cat-row-9)<br>template<class T>   struct is_rvalue_reference; | T is an rvalue reference type ([[dcl.ref]](dcl.ref "9.3.4.3 References")) |  |
| [ð](#tab:meta.unary.cat-row-10)<br>template<class T>   struct is_member_object_pointer; | T is a pointer to data member |  |
| [ð](#tab:meta.unary.cat-row-11)<br>template<class T>   struct is_member_function_pointer; | T is a pointer to member function |  |
| [ð](#tab:meta.unary.cat-row-12)<br>template<class T>   struct is_enum; | T is an enumeration type ([[basic.compound]](basic.compound "6.9.4 Compound types")) |  |
| [ð](#tab:meta.unary.cat-row-13)<br>template<class T>   struct is_union; | T is a union type ([[basic.compound]](basic.compound "6.9.4 Compound types")) |  |
| [ð](#tab:meta.unary.cat-row-14)<br>template<class T>   struct is_class; | T is a non-union class type ([[basic.compound]](basic.compound "6.9.4 Compound types")) |  |
| [ð](#tab:meta.unary.cat-row-15)<br>template<class T>   struct is_function; | T is a function type ([[basic.compound]](basic.compound "6.9.4 Compound types")) |  |
| [ð](#tab:meta.unary.cat-row-16)<br>template<class T>   struct is_reflection; | T is std​::​meta​::​info ([[basic.fundamental]](basic.fundamental "6.9.2 Fundamental types")) |  |

#### [21.3.6.3](#meta.unary.comp) Composite type traits [[meta.unary.comp]](meta.unary.comp)

[1](#meta.unary.comp-1)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L1014)

The templates specified in Table [53](#tab:meta.unary.comp "Table 53: Composite type category predicates") provide convenient compositions of the primary type categories,
corresponding to the descriptions given in subclause [[basic.types]](basic.types "6.9 Types")[.](#meta.unary.comp-1.sentence-1)

[2](#meta.unary.comp-2)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L1019)

For any given type T, the result of applying one of these templates toT and to cv T shall yield the same result[.](#meta.unary.comp-2.sentence-1)

Table [53](#tab:meta.unary.comp) — Composite type category predicates [[tab:meta.unary.comp]](./tab:meta.unary.comp)  

| [ð](#tab:meta.unary.comp-row-1)<br>**Template** | **Condition** | **Comments** |
| --- | --- | --- |
| [ð](#tab:meta.unary.comp-row-2)<br>template<class T>   struct is_reference; | T is an lvalue reference or an rvalue reference |  |
| [ð](#tab:meta.unary.comp-row-3)<br>template<class T>   struct is_arithmetic; | T is an arithmetic type ([[basic.fundamental]](basic.fundamental "6.9.2 Fundamental types")) |  |
| [ð](#tab:meta.unary.comp-row-4)<br>template<class T>   struct is_fundamental; | T is a fundamental type ([[basic.fundamental]](basic.fundamental "6.9.2 Fundamental types")) |  |
| [ð](#tab:meta.unary.comp-row-5)<br>template<class T>   struct is_object; | T is an object type ([[basic.types.general]](basic.types.general#term.object.type "6.9.1 General")) |  |
| [ð](#tab:meta.unary.comp-row-6)<br>template<class T>   struct is_scalar; | T is a scalar type ([[basic.types.general]](basic.types.general#term.scalar.type "6.9.1 General")) |  |
| [ð](#tab:meta.unary.comp-row-7)<br>template<class T>   struct is_compound; | T is a compound type ([[basic.compound]](basic.compound "6.9.4 Compound types")) |  |
| [ð](#tab:meta.unary.comp-row-8)<br>template<class T>   struct is_member_pointer; | T is a pointer-to-member type ([[basic.compound]](basic.compound "6.9.4 Compound types")) |  |

#### [21.3.6.4](#meta.unary.prop) Type properties [[meta.unary.prop]](meta.unary.prop)

[1](#meta.unary.prop-1)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L1063)

The templates specified in Table [54](#tab:meta.unary.prop "Table 54: Type property predicates") provide access to some of the more important properties of types[.](#meta.unary.prop-1.sentence-1)

[2](#meta.unary.prop-2)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L1067)

It is unspecified whether the library defines any full or partial
specializations of any of these templates[.](#meta.unary.prop-2.sentence-1)

[3](#meta.unary.prop-3)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L1071)

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[.](#meta.unary.prop-3.sentence-1)

[4](#meta.unary.prop-4)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L1078)

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 ([[depr.meta.types]](depr.meta.types#term.trivial.type "D.13 Deprecated type traits"), [[special]](special "11.4.4 Special member functions")) function call
that is not an odr-use ([[basic.def.odr]](basic.def.odr#term.odr.use "6.3 One-definition rule")) of declval in the context of the corresponding definition
notwithstanding the restrictions of [[declval]](declval "22.2.6 Function template declval")[.](#meta.unary.prop-4.sentence-1)

[5](#meta.unary.prop-5)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L1086)

For the purpose of defining the templates in this subclause,
let *VAL*<T> for some type T be
an expression defined as follows:

- [(5.1)](#meta.unary.prop-5.1)

  If T is a reference or function type,*VAL*<T> is an expression
with the same type and value category as declval<T>()[.](#meta.unary.prop-5.1.sentence-1)

- [(5.2)](#meta.unary.prop-5.2)

  Otherwise, *VAL*<T> is a prvalue
that initially has type T[.](#meta.unary.prop-5.2.sentence-1)
  [*Note [1](#meta.unary.prop-note-1)*:
  If T is cv-qualified,
the cv-qualification is subject to adjustment ([[expr.type]](expr.type "7.2.2 Type"))[.](#meta.unary.prop-5.2.sentence-2)
 â *end note*]

Table [54](#tab:meta.unary.prop) — Type property predicates [[tab:meta.unary.prop]](./tab:meta.unary.prop)  

| [ð](#tab:meta.unary.prop-row-1)<br>**Template** | **Condition** | **Preconditions** |
| --- | --- | --- |
| [ð](#tab:meta.unary.prop-row-2)<br>template<class T>   struct is_const; | T is const-qualified ([[basic.type.qualifier]](basic.type.qualifier "6.9.5 CV-qualifiers")) |  |
| [ð](#tab:meta.unary.prop-row-3)<br>template<class T>   struct is_volatile; | T is volatile-qualified ([[basic.type.qualifier]](basic.type.qualifier "6.9.5 CV-qualifiers")) |  |
| [ð](#tab:meta.unary.prop-row-4)<br>template<class T>   struct is_trivially_copyable; | T is a trivially copyable type ([[basic.types.general]](basic.types.general#term.trivially.copyable.type "6.9.1 General")) | remove_all_extents_t<T> shall be a complete type or  cv void[.](#tab:meta.unary.prop-row-4-column-3-sentence-1) |
| [ð](#tab:meta.unary.prop-row-5)<br>template<class T>   struct is_trivially_relocatable; | T is a trivially relocatable type ([[basic.types.general]](basic.types.general "6.9.1 General")) | remove_all_extents_t<T> shall be a complete type or  cv void[.](#tab:meta.unary.prop-row-5-column-3-sentence-1) |
| [ð](#tab:meta.unary.prop-row-6)<br>template<class T>   struct is_replaceable; | T is a replaceable type ([[basic.types.general]](basic.types.general "6.9.1 General")) | remove_all_extents_t<T> shall be a complete type or  cv void[.](#tab:meta.unary.prop-row-6-column-3-sentence-1) |
| [ð](#tab:meta.unary.prop-row-7)<br>template<class T>   struct is_standard_layout; | T is a standard-layout type ([[basic.types.general]](basic.types.general#term.standard.layout.type "6.9.1 General")) | remove_all_extents_t<T> shall be a complete  type or cv void[.](#tab:meta.unary.prop-row-7-column-3-sentence-1) |
| [ð](#tab:meta.unary.prop-row-8)<br>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[.](#tab:meta.unary.prop-row-8-column-2-sentence-1) | If T is a non-union class type, T shall be a complete type[.](#tab:meta.unary.prop-row-8-column-3-sentence-1) |
| [ð](#tab:meta.unary.prop-row-9)<br>template<class T>   struct is_polymorphic; | T is a polymorphic class ([[class.virtual]](class.virtual "11.7.3 Virtual functions")) | If T is a non-union class type, T shall be a complete type[.](#tab:meta.unary.prop-row-9-column-3-sentence-1) |
| [ð](#tab:meta.unary.prop-row-10)<br>template<class T>   struct is_abstract; | T is an abstract class ([[class.abstract]](class.abstract "11.7.4 Abstract classes")) | If T is a non-union class type, T shall be a complete type[.](#tab:meta.unary.prop-row-10-column-3-sentence-1) |
| [ð](#tab:meta.unary.prop-row-11)<br>template<class T>   struct is_final; | T is a class type marked with the [*class-property-specifier*](class.pre#nt:class-property-specifier "11.1 Preamble [class.pre]")final ([[class.pre]](class.pre "11.1 Preamble"))[.](#tab:meta.unary.prop-row-11-column-2-sentence-1)<br>[*Note [2](#tab:meta.unary.prop-row-11-column-2-note-2)*:<br>A union is a class type that  can be marked with final[.](#tab:meta.unary.prop-row-11-column-2-sentence-2) â *end note*] | If T is a class type, T shall be a complete type[.](#tab:meta.unary.prop-row-11-column-3-sentence-1) |
| [ð](#tab:meta.unary.prop-row-12)<br>template<class T>   struct [is_aggregate](#lib:is_aggregate "21.3.6.4 Type properties [meta.unary.prop]"); | T is an aggregate type ([[dcl.init.aggr]](dcl.init.aggr "9.5.2 Aggregates")) | T shall be an array type, a complete type, or cv void[.](#tab:meta.unary.prop-row-12-column-3-sentence-1) |
| [ð](#tab:meta.unary.prop-row-13)<br>template<class T>   struct is_consteval_only; | T is consteval-only ([[basic.types.general]](basic.types.general "6.9.1 General")) | remove_all_extents_t<T> shall be a complete type or cv void[.](#tab:meta.unary.prop-row-13-column-3-sentence-1) |
| [ð](#tab:meta.unary.prop-row-14)<br>template<class T>   struct is_signed; | If is_arithmetic_v<T> is true, the same result as  T(-1) < T(0);  otherwise, false |  |
| [ð](#tab:meta.unary.prop-row-15)<br>template<class T>   struct [is_unsigned](#lib:is_unsigned "21.3.6.4 Type properties [meta.unary.prop]"); | If is_arithmetic_v<T> is true, the same result as  T(0) < T(-1);  otherwise, false |  |
| [ð](#tab:meta.unary.prop-row-16)<br>template<class T>   struct [is_bounded_array](#lib:is_bounded_array "21.3.6.4 Type properties [meta.unary.prop]"); | T is an array type of known bound ([[dcl.array]](dcl.array "9.3.4.5 Arrays")) |  |
| [ð](#tab:meta.unary.prop-row-17)<br>template<class T>   struct is_unbounded_array; | T is an array type of unknown bound ([[dcl.array]](dcl.array "9.3.4.5 Arrays")) |  |
| [ð](#tab:meta.unary.prop-row-18)<br>template<class T>   struct is_scoped_enum; | T is a scoped enumeration ([[dcl.enum]](dcl.enum "9.8.1 Enumeration declarations")) |  |
| [ð](#tab:meta.unary.prop-row-19)<br>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[.](#tab:meta.unary.prop-row-19-column-3-sentence-1) |
| [ð](#tab:meta.unary.prop-row-20)<br>template<class T>   struct is_default_constructible; | is_constructible_v<T> is true[.](#tab:meta.unary.prop-row-20-column-2-sentence-1) | T shall be a complete type, cv void,  or an array of unknown bound[.](#tab:meta.unary.prop-row-20-column-3-sentence-1) |
| [ð](#tab:meta.unary.prop-row-21)<br>template<class T>   struct is_copy_constructible; | For a referenceable type T ([[defns.referenceable]](defns.referenceable "3.45 referenceable type")), the same result as  is_constructible_v<T, const T&>, otherwise false[.](#tab:meta.unary.prop-row-21-column-2-sentence-1) | T shall be a complete type, cv void,  or an array of unknown bound[.](#tab:meta.unary.prop-row-21-column-3-sentence-1) |
| [ð](#tab:meta.unary.prop-row-22)<br>template<class T>   struct is_move_constructible; | For a referenceable type T, the same result as  is_constructible_v<T, T&&>, otherwise false[.](#tab:meta.unary.prop-row-22-column-2-sentence-1) | T shall be a complete type, cv void,  or an array of unknown bound[.](#tab:meta.unary.prop-row-22-column-3-sentence-1) |
| [ð](#tab:meta.unary.prop-row-23)<br>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]](expr.context#term.unevaluated.operand "7.2.3 Context dependence"))[.](#tab:meta.unary.prop-row-23-column-2-sentence-1)<br>Access checking is performed as if in a context  unrelated to T and U[.](#tab:meta.unary.prop-row-23-column-2-sentence-2)<br>Only the validity of the immediate context  of the assignment expression is considered[.](#tab:meta.unary.prop-row-23-column-2-sentence-3)<br>[*Note [3](#tab:meta.unary.prop-row-23-column-2-note-3)*:<br>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[.](#tab:meta.unary.prop-row-23-column-2-sentence-4)<br>Such side effects are not in the âimmediate  contextâ and can result in the program being ill-formed[.](#tab:meta.unary.prop-row-23-column-2-sentence-5) â *end note*] | T and U shall be complete types, cv void,  or arrays of unknown bound[.](#tab:meta.unary.prop-row-23-column-3-sentence-1) |
| [ð](#tab:meta.unary.prop-row-24)<br>template<class T>   struct is_copy_assignable; | For a referenceable type T, the same result as  is_assignable_v<T&, const T&>, otherwise false[.](#tab:meta.unary.prop-row-24-column-2-sentence-1) | T shall be a complete type, cv void,  or an array of unknown bound[.](#tab:meta.unary.prop-row-24-column-3-sentence-1) |
| [ð](#tab:meta.unary.prop-row-25)<br>template<class T>   struct is_move_assignable; | For a referenceable type T, the same result as  is_assignable_v<T&, T&&>, otherwise false[.](#tab:meta.unary.prop-row-25-column-2-sentence-1) | T shall be a complete type, cv void,  or an array of unknown bound[.](#tab:meta.unary.prop-row-25-column-3-sentence-1) |
| [ð](#tab:meta.unary.prop-row-26)<br>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]](expr.context#term.unevaluated.operand "7.2.3 Context dependence"))  in an overload-resolution context  for swappable values ([[swappable.requirements]](swappable.requirements "16.4.4.3 Swappable requirements"))[.](#tab:meta.unary.prop-row-26-column-2-sentence-1)<br>Access checking is performed as if in a context  unrelated to T and U[.](#tab:meta.unary.prop-row-26-column-2-sentence-2)<br>Only the validity of the immediate context  of the swap expressions is considered[.](#tab:meta.unary.prop-row-26-column-2-sentence-3)<br>[*Note [4](#tab:meta.unary.prop-row-26-column-2-note-4)*:<br>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[.](#tab:meta.unary.prop-row-26-column-2-sentence-4)<br>Such side effects are not in the âimmediate contextâ and  can result in the program being ill-formed[.](#tab:meta.unary.prop-row-26-column-2-sentence-5) â *end note*] | T and U shall be complete types,  cv void, or  arrays of unknown bound[.](#tab:meta.unary.prop-row-26-column-3-sentence-1) |
| [ð](#tab:meta.unary.prop-row-27)<br>template<class T>   struct is_swappable; | For a referenceable type T,  the same result as is_swappable_with_v<T&, T&>,  otherwise false[.](#tab:meta.unary.prop-row-27-column-2-sentence-1) | T shall be a complete type,  cv void, or  an array of unknown bound[.](#tab:meta.unary.prop-row-27-column-3-sentence-1) |
| [ð](#tab:meta.unary.prop-row-28)<br>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]](expr.context#term.unevaluated.operand "7.2.3 Context dependence")),  where U is  remove_all_extents_t<T>[.](#tab:meta.unary.prop-row-28-column-2-sentence-1) | T shall be a complete type, cv void,  or an array of unknown bound[.](#tab:meta.unary.prop-row-28-column-3-sentence-1) |
| [ð](#tab:meta.unary.prop-row-29)<br>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 ([[depr.meta.types]](depr.meta.types#term.trivial.type "D.13 Deprecated type traits"), [[special]](special "11.4.4 Special member functions"))[.](#tab:meta.unary.prop-row-29-column-2-sentence-1) | T and all types in the template parameter pack Args shall be complete types,  cv void, or arrays of unknown bound[.](#tab:meta.unary.prop-row-29-column-3-sentence-1) |
| [ð](#tab:meta.unary.prop-row-30)<br>template<class T>   struct is_trivially_default_constructible; | is_trivially_constructible_v<T> is true[.](#tab:meta.unary.prop-row-30-column-2-sentence-1) | T shall be a complete type,  cv void, or an array of unknown  bound[.](#tab:meta.unary.prop-row-30-column-3-sentence-1) |
| [ð](#tab:meta.unary.prop-row-31)<br>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[.](#tab:meta.unary.prop-row-31-column-2-sentence-1) | T shall be a complete type,  cv void, or an array of unknown  bound[.](#tab:meta.unary.prop-row-31-column-3-sentence-1) |
| [ð](#tab:meta.unary.prop-row-32)<br>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[.](#tab:meta.unary.prop-row-32-column-2-sentence-1) | T shall be a complete type,  cv void, or an array of unknown  bound[.](#tab:meta.unary.prop-row-32-column-3-sentence-1) |
| [ð](#tab:meta.unary.prop-row-33)<br>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 ([[depr.meta.types]](depr.meta.types#term.trivial.type "D.13 Deprecated type traits"), [[special]](special "11.4.4 Special member functions"))[.](#tab:meta.unary.prop-row-33-column-2-sentence-1) | T and U shall be complete types, cv void,  or arrays of unknown bound[.](#tab:meta.unary.prop-row-33-column-3-sentence-1) |
| [ð](#tab:meta.unary.prop-row-34)<br>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[.](#tab:meta.unary.prop-row-34-column-2-sentence-1) | T shall be a complete type,  cv void, or an array of unknown  bound[.](#tab:meta.unary.prop-row-34-column-3-sentence-1) |
| [ð](#tab:meta.unary.prop-row-35)<br>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[.](#tab:meta.unary.prop-row-35-column-2-sentence-1) | T shall be a complete type,  cv void, or an array of unknown bound[.](#tab:meta.unary.prop-row-35-column-3-sentence-1) |
| [ð](#tab:meta.unary.prop-row-36)<br>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[.](#tab:meta.unary.prop-row-36-column-2-sentence-1) | T shall be a complete type,  cv void, or an array of unknown  bound[.](#tab:meta.unary.prop-row-36-column-3-sentence-1) |
| [ð](#tab:meta.unary.prop-row-37)<br>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]](expr.unary.noexcept "7.6.2.7 noexcept operator"))[.](#tab:meta.unary.prop-row-37-column-2-sentence-1) | T and all types in the template parameter pack Args  shall be complete types, cv void,  or arrays of unknown bound[.](#tab:meta.unary.prop-row-37-column-3-sentence-1) |
| [ð](#tab:meta.unary.prop-row-38)<br>template<class T>   struct is_nothrow_default_constructible; | is_nothrow_constructible_v<T> is true[.](#tab:meta.unary.prop-row-38-column-2-sentence-1) | T shall be a complete type,  cv void, or an array of unknown  bound[.](#tab:meta.unary.prop-row-38-column-3-sentence-1) |
| [ð](#tab:meta.unary.prop-row-39)<br>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[.](#tab:meta.unary.prop-row-39-column-2-sentence-1) | T shall be a complete type,  cv void, or an array of unknown  bound[.](#tab:meta.unary.prop-row-39-column-3-sentence-1) |
| [ð](#tab:meta.unary.prop-row-40)<br>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[.](#tab:meta.unary.prop-row-40-column-2-sentence-1) | T shall be a complete type,  cv void, or an array of unknown bound[.](#tab:meta.unary.prop-row-40-column-3-sentence-1) |
| [ð](#tab:meta.unary.prop-row-41)<br>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]](expr.unary.noexcept "7.6.2.7 noexcept operator"))[.](#tab:meta.unary.prop-row-41-column-2-sentence-1) | T and U shall be complete types, cv void,  or arrays of unknown bound[.](#tab:meta.unary.prop-row-41-column-3-sentence-1) |
| [ð](#tab:meta.unary.prop-row-42)<br>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[.](#tab:meta.unary.prop-row-42-column-2-sentence-1) | T shall be a complete type,  cv void, or an array of unknown  bound[.](#tab:meta.unary.prop-row-42-column-3-sentence-1) |
| [ð](#tab:meta.unary.prop-row-43)<br>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[.](#tab:meta.unary.prop-row-43-column-2-sentence-1) | T shall be a complete type,  cv void, or an array of unknown  bound[.](#tab:meta.unary.prop-row-43-column-3-sentence-1) |
| [ð](#tab:meta.unary.prop-row-44)<br>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]](expr.unary.noexcept "7.6.2.7 noexcept operator"))[.](#tab:meta.unary.prop-row-44-column-2-sentence-1) | T and U shall be complete types,  cv void, or  arrays of unknown bound[.](#tab:meta.unary.prop-row-44-column-3-sentence-1) |
| [ð](#tab:meta.unary.prop-row-45)<br>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[.](#tab:meta.unary.prop-row-45-column-2-sentence-1) | T shall be a complete type,  cv void, or  an array of unknown bound[.](#tab:meta.unary.prop-row-45-column-3-sentence-1) |
| [ð](#tab:meta.unary.prop-row-46)<br>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]](expr.unary.noexcept "7.6.2.7 noexcept operator"))[.](#tab:meta.unary.prop-row-46-column-2-sentence-1) | T shall be a complete type,  cv void, or an array of unknown  bound[.](#tab:meta.unary.prop-row-46-column-3-sentence-1) |
| [ð](#tab:meta.unary.prop-row-47)<br>template<class T>   struct is_nothrow_relocatable; | is_trivially_relocatable_v<T> ||(is_nothrow_move_constructible_v<remove_all_extents_t<T>> && is_nothrow_destructible_v<remove_all_extents_t<T>>) | remove_all_extents_t<T> shall be a complete type or  cv void[.](#tab:meta.unary.prop-row-47-column-3-sentence-1) |
| [ð](#tab:meta.unary.prop-row-48)<br>template<class T>   struct is_implicit_lifetime; | T is an implicit-lifetime type ([[basic.types.general]](basic.types.general#term.implicit.lifetime.type "6.9.1 General"))[.](#tab:meta.unary.prop-row-48-column-2-sentence-1) | T shall be an array type,  a complete type, or cv void[.](#tab:meta.unary.prop-row-48-column-3-sentence-1) |
| [ð](#tab:meta.unary.prop-row-49)<br>template<class T>   struct has_virtual_destructor; | T has a virtual destructor ([[class.dtor]](class.dtor "11.4.7 Destructors")) | If T is a non-union class type, T shall be a complete type[.](#tab:meta.unary.prop-row-49-column-3-sentence-1) |
| [ð](#tab:meta.unary.prop-row-50)<br>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*[.](#tab:meta.unary.prop-row-50-column-2-sentence-1) | remove_all_extents_t<T> shall be a complete type or  cv void[.](#tab:meta.unary.prop-row-50-column-3-sentence-1) |
| [ð](#tab:meta.unary.prop-row-51)<br>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]](class.temporary "6.8.7 Temporary objects"))[.](#tab:meta.unary.prop-row-51-column-2-sentence-1)<br>Access checking is performed as if in  a context unrelated to T and U[.](#tab:meta.unary.prop-row-51-column-2-sentence-2)<br>Only the validity of the immediate context of  the variable initialization is considered[.](#tab:meta.unary.prop-row-51-column-2-sentence-3)<br>[*Note [5](#tab:meta.unary.prop-row-51-column-2-note-5)*:<br>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[.](#tab:meta.unary.prop-row-51-column-2-sentence-4)<br>Such effects are not in the âimmediate contextâ and  can result in the program being ill-formed[.](#tab:meta.unary.prop-row-51-column-2-sentence-5) â *end note*] | T and U shall be  complete types, cv void, or arrays of unknown bound[.](#tab:meta.unary.prop-row-51-column-3-sentence-1) |
| [ð](#tab:meta.unary.prop-row-52)<br>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]](class.temporary "6.8.7 Temporary objects"))[.](#tab:meta.unary.prop-row-52-column-2-sentence-1)<br>Access checking is performed as if in  a context unrelated to T and U[.](#tab:meta.unary.prop-row-52-column-2-sentence-2)<br>Only the validity of the immediate context of  the variable initialization is considered[.](#tab:meta.unary.prop-row-52-column-2-sentence-3)<br>[*Note [6](#tab:meta.unary.prop-row-52-column-2-note-6)*:<br>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[.](#tab:meta.unary.prop-row-52-column-2-sentence-4)<br>Such effects are not in the âimmediate contextâ and  can result in the program being ill-formed[.](#tab:meta.unary.prop-row-52-column-2-sentence-5) â *end note*] | T and U shall be  complete types, cv void, or arrays of unknown bound[.](#tab:meta.unary.prop-row-52-column-3-sentence-1) |

[6](#meta.unary.prop-6)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L1590)

[*Example [1](#meta.unary.prop-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*]

[7](#meta.unary.prop-7)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L1601)

[*Example [2](#meta.unary.prop-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*]

[8](#meta.unary.prop-8)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L1611)

[*Example [3](#meta.unary.prop-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*]

[9](#meta.unary.prop-9)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L1628)

The predicate condition for a template specializationis_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](#meta.unary.prop-note-7)*:

These tokens are never interpreted as a function declaration[.](#meta.unary.prop-9.sentence-2)

â *end note*]

Access checking is performed as if in a context unrelated to T and any of the Args[.](#meta.unary.prop-9.sentence-3)

Only the validity of the immediate context of the
variable initialization is considered[.](#meta.unary.prop-9.sentence-4)

[*Note [8](#meta.unary.prop-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[.](#meta.unary.prop-9.sentence-5)

Such side effects are not in the
âimmediate contextâ and can result in the program being ill-formed[.](#meta.unary.prop-9.sentence-6)

â *end note*]

[10](#meta.unary.prop-10)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L1652)

The predicate condition for a template specializationhas_unique_object_representations<T> shall be satisfied if and only if

- [(10.1)](#meta.unary.prop-10.1)

T is trivially copyable, and

- [(10.2)](#meta.unary.prop-10.2)

any two objects of type T with the same value
have the same object representation, where
  * [(10.2.1)](#meta.unary.prop-10.2.1)

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

  * [(10.2.2)](#meta.unary.prop-10.2.2)

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[.](#meta.unary.prop-10.sentence-1)

The set of scalar types for which this condition holds isimplementation-defined[.](#meta.unary.prop-10.sentence-2)

[*Note [9](#meta.unary.prop-note-9)*:

If a type has padding bits, the condition does not hold;
otherwise, the condition holds true for integral types[.](#meta.unary.prop-10.sentence-3)

â *end note*]

### [21.3.7](#meta.unary.prop.query) Type property queries [[meta.unary.prop.query]](meta.unary.prop.query)

[1](#meta.unary.prop.query-1)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L1676)

The templates specified in Table [55](#tab:meta.unary.prop.query "Table 55: Type property queries") may be used to query properties of types at compile time[.](#meta.unary.prop.query-1.sentence-1)

Table [55](#tab:meta.unary.prop.query) — Type property queries [[tab:meta.unary.prop.query]](./tab:meta.unary.prop.query)  

| [ð](#tab:meta.unary.prop.query-row-1)<br>**Template** | **Value** |
| --- | --- |
| [ð](#tab:meta.unary.prop.query-row-2)<br>template<class T>   struct alignment_of; | alignof(T)[.](#tab:meta.unary.prop.query-row-2-column-2-sentence-1)<br>   *Mandates*: alignof(T) is a valid expression ([[expr.alignof]](expr.alignof "7.6.2.6 Alignof")) |
| [ð](#tab:meta.unary.prop.query-row-3)<br>template<class T>   struct rank; | If T is an array type, an integer value representing  the number of dimensions of T; otherwise, 0[.](#tab:meta.unary.prop.query-row-3-column-2-sentence-1) |
| [ð](#tab:meta.unary.prop.query-row-4)<br>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]](dcl.array "9.3.4.5 Arrays")) of the Ith dimension of T, where indexing of I is zero-based |

[2](#meta.unary.prop.query-2)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L1713)

Each of these templates shall be a [*Cpp17UnaryTypeTrait*](#:Cpp17UnaryTypeTrait "21.3.2 Requirements [meta.rqmts]") ([[meta.rqmts]](#meta.rqmts "21.3.2 Requirements")) with a
base characteristic of integral_constant<size_t, Value>[.](#meta.unary.prop.query-2.sentence-1)

[3](#meta.unary.prop.query-3)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L1717)

[*Example [1](#meta.unary.prop.query-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*]

[4](#meta.unary.prop.query-4)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L1727)

[*Example [2](#meta.unary.prop.query-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.8](#meta.rel) Relationships between types [[meta.rel]](meta.rel)

[1](#meta.rel-1)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L1744)

The templates specified in Table [56](#tab:meta.rel "Table 56: Type relationship predicates") may be used to query relationships between types at compile time[.](#meta.rel-1.sentence-1)

[2](#meta.rel-2)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L1748)

Each of these templates shall be a[*Cpp17BinaryTypeTrait*](#:Cpp17BinaryTypeTrait "21.3.2 Requirements [meta.rqmts]") ([[meta.rqmts]](#meta.rqmts "21.3.2 Requirements"))
with a base characteristic oftrue_type if the corresponding condition is true, otherwisefalse_type[.](#meta.rel-2.sentence-1)

[3](#meta.rel-3)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L1755)

Let *ELEMS-OF*(T) be the parameter packget<*N*>(declval<T>()), where *N* is the pack ofsize_t template arguments of the specialization ofindex_sequence denoted bymake_index_sequence<tuple_size_v<remove_reference_t<T>>>[.](#meta.rel-3.sentence-1)

Table [56](#tab:meta.rel) — Type relationship predicates [[tab:meta.rel]](./tab:meta.rel)  

| [ð](#tab:meta.rel-row-1)<br>**Template** | **Condition** | **Comments** |
| --- | --- | --- |
| [ð](#tab:meta.rel-row-2)<br>template<class T, class U>   struct [is_same](#lib:is_same "21.3.8 Relationships between types [meta.rel]"); | T and U name the same type with the same cv-qualifications |  |
| [ð](#tab:meta.rel-row-3)<br>template<class Base, class Derived>   struct is_base_of; | Base is a base class of Derived ([[class.derived]](class.derived "11.7 Derived classes"))  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[.](#tab:meta.rel-row-3-column-3-sentence-1)<br>[*Note [1](#tab:meta.rel-row-3-column-3-note-1)*:<br>Base classes that are private, protected, or ambiguous  are, nonetheless, base classes[.](#tab:meta.rel-row-3-column-3-sentence-2) â *end note*] |
| [ð](#tab:meta.rel-row-4)<br>template<class Base, class Derived>   struct is_virtual_base_of; | Base is a virtual base class of Derived ([[class.mi]](class.mi "11.7.2 Multiple base classes"))  without regard to cv-qualifiers[.](#tab:meta.rel-row-4-column-2-sentence-1) | If Base and  Derived are non-union class types,  Derived shall be a complete type[.](#tab:meta.rel-row-4-column-3-sentence-1)<br>[*Note [2](#tab:meta.rel-row-4-column-3-note-2)*:<br>Virtual base classes that are private, protected, or ambiguous  are, nonetheless, virtual base classes[.](#tab:meta.rel-row-4-column-3-sentence-2) â *end note*]<br>[*Note [3](#tab:meta.rel-row-4-column-3-note-3)*:<br>A class is never a virtual base class of itself[.](#tab:meta.rel-row-4-column-3-sentence-3) â *end note*] |
| [ð](#tab:meta.rel-row-5)<br>template<class From, class To>   struct is_convertible; | *see below* | From and To shall be complete types,  cv void, or arrays of unknown bound[.](#tab:meta.rel-row-5-column-3-sentence-1) |
| [ð](#tab:meta.rel-row-6)<br>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]](expr.unary.noexcept "7.6.2.7 noexcept operator")) | From and To shall be complete types,  cv void, or arrays of unknown bound[.](#tab:meta.rel-row-6-column-3-sentence-1) |
| [ð](#tab:meta.rel-row-7)<br>template<class T, class U>   struct is_layout_compatible; | T and U are layout-compatible ([[basic.types.general]](basic.types.general#term.layout.compatible.type "6.9.1 General")) | T and U shall be complete types,  cv void,  or arrays of unknown bound[.](#tab:meta.rel-row-7-column-3-sentence-1) |
| [ð](#tab:meta.rel-row-8)<br>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]](basic.compound "6.9.4 Compound types")) with  its Base subobject,  or Base and Derived are not unions  and name the same class type  without regard to cv-qualifiers[.](#tab:meta.rel-row-8-column-2-sentence-1) | 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[.](#tab:meta.rel-row-8-column-3-sentence-1) |
| [ð](#tab:meta.rel-row-9)<br>template<class Fn, class... ArgTypes>   struct is_invocable; | The expression *INVOKE*(declval<Fn>(), declval<ArgTypes>()...) ([[func.require]](func.require "22.10.4 Requirements"))  is well-formed when treated as an unevaluated operand ([[expr.context]](expr.context#term.unevaluated.operand "7.2.3 Context dependence")) | Fn and all types in the template parameter pack ArgTypes  shall be complete types, cv void, or  arrays of unknown bound[.](#tab:meta.rel-row-9-column-3-sentence-1) |
| [ð](#tab:meta.rel-row-10)<br>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[.](#tab:meta.rel-row-10-column-3-sentence-1) |
| [ð](#tab:meta.rel-row-11)<br>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]](expr.unary.noexcept "7.6.2.7 noexcept operator")) | Fn and all types in the template parameter pack ArgTypes  shall be complete types, cv void, or  arrays of unknown bound[.](#tab:meta.rel-row-11-column-3-sentence-1) |
| [ð](#tab:meta.rel-row-12)<br>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]](expr.unary.noexcept "7.6.2.7 noexcept operator")) | Fn, R, and all types in the template parameter pack ArgTypes  shall be complete types, cv void, or  arrays of unknown bound[.](#tab:meta.rel-row-12-column-3-sentence-1) |
| [ð](#tab:meta.rel-row-13)<br>template<class Fn, class Tuple>   struct is_applicable; | [*tuple-like*](tuple.like#concept:tuple-like "22.4.3 Concept tuple-like [tuple.like]")<Tuple> is true and  the expression  *INVOKE*(declval<Fn>(), *ELEMS-OF*(Tuple)...)  is well-formed when treated as an unevaluated operand[.](#tab:meta.rel-row-13-column-2-sentence-1) | Fn and Tuple  shall be complete types, cv void, or  arrays of unknown bound[.](#tab:meta.rel-row-13-column-3-sentence-1) |
| [ð](#tab:meta.rel-row-14)<br>template<class Fn, class Tuple>   struct is_nothrow_applicable; | is_applicable_v<   Fn, Tuple> is true and  the expression *INVOKE*(declval<Fn>(), *ELEMS-OF*(Tuple)...)  is known not to throw any exceptions ([[expr.unary.noexcept]](expr.unary.noexcept "7.6.2.7 noexcept operator"))[.](#tab:meta.rel-row-14-column-2-sentence-1) | Fn and Tuple  shall be complete types, cv void, or  arrays of unknown bound[.](#tab:meta.rel-row-14-column-3-sentence-1) |

[4](#meta.rel-4)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L1908)

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 ([[depr.meta.types]](depr.meta.types#term.trivial.type "D.13 Deprecated type traits"), [[special]](special "11.4.4 Special member functions")) function call
that is not an odr-use ([[basic.def.odr]](basic.def.odr#term.odr.use "6.3 One-definition rule")) of declval in the context of the corresponding definition
notwithstanding the restrictions of [[declval]](declval "22.2.6 Function template declval")[.](#meta.rel-4.sentence-1)

[5](#meta.rel-5)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L1916)

[*Example [1](#meta.rel-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*]

[6](#meta.rel-6)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L1936)

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](#meta.rel-note-4)*:

This requirement gives well-defined results for reference types,
array types, function types, and cv void[.](#meta.rel-6.sentence-2)

â *end note*]

Access checking is performed
in a context unrelated to To and From[.](#meta.rel-6.sentence-3)

Only the validity of
the immediate context of the [*expression*](expr.comma#nt:expression "7.6.20 Comma operator [expr.comma]") of the return statement ([[stmt.return]](stmt.return "8.8.4 The return statement"))
(including initialization of the returned object or reference) is considered[.](#meta.rel-6.sentence-4)

[*Note [5](#meta.rel-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[.](#meta.rel-6.sentence-5)

Such
side effects are not in the âimmediate contextâ and can result in the program
being ill-formed[.](#meta.rel-6.sentence-6)

â *end note*]

### [21.3.9](#meta.trans) Transformations between types [[meta.trans]](meta.trans)

#### [21.3.9.1](#meta.trans.general) General [[meta.trans.general]](meta.trans.general)

[1](#meta.trans.general-1)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L1966)

Subclause [[meta.trans]](#meta.trans "21.3.9 Transformations between types") contains templates that may be used to transform one
type to another following some predefined rule[.](#meta.trans.general-1.sentence-1)

[2](#meta.trans.general-2)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L1970)

Each of the templates in [[meta.trans]](#meta.trans "21.3.9 Transformations between types") shall be a[*Cpp17TransformationTrait*](#:Cpp17TransformationTrait "21.3.2 Requirements [meta.rqmts]") ([[meta.rqmts]](#meta.rqmts "21.3.2 Requirements"))[.](#meta.trans.general-2.sentence-1)

#### [21.3.9.2](#meta.trans.cv) Const-volatile modifications [[meta.trans.cv]](meta.trans.cv)

[1](#meta.trans.cv-1)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L1976)

The templates specified in Table [57](#tab:meta.trans.cv "Table 57: Const-volatile modifications") add or remove cv-qualifications ([[basic.type.qualifier]](basic.type.qualifier "6.9.5 CV-qualifiers"))[.](#meta.trans.cv-1.sentence-1)

Table [57](#tab:meta.trans.cv) — Const-volatile modifications [[tab:meta.trans.cv]](./tab:meta.trans.cv)  

| [ð](#tab:meta.trans.cv-row-1)<br>**Template** | **Comments** |
| --- | --- |
| [ð](#tab:meta.trans.cv-row-2)<br>template<class T>   struct remove_const; | The member typedef type denotes the type formed  by removing any top-level const-qualifier from T[.](#tab:meta.trans.cv-row-2-column-2-sentence-1)<br>[*Example [1](#tab:meta.trans.cv-row-2-column-2-example-1)*:<br>remove_const_t<const volatile int> evaluates  to volatile int, whereas remove_const_t<const int*> evaluates to  const int*[.](#tab:meta.trans.cv-row-2-column-2-sentence-2) â *end example*] |
| [ð](#tab:meta.trans.cv-row-3)<br>template<class T>   struct remove_volatile; | The member typedef type denotes the type formed  by removing any top-level volatile-qualifier from T[.](#tab:meta.trans.cv-row-3-column-2-sentence-1)<br>[*Example [2](#tab:meta.trans.cv-row-3-column-2-example-2)*:<br>remove_volatile_t<const volatile int>  evaluates to const int,  whereas remove_volatile_t<volatile int*> evaluates to volatile int*[.](#tab:meta.trans.cv-row-3-column-2-sentence-2) â *end example*] |
| [ð](#tab:meta.trans.cv-row-4)<br>template<class T>   struct remove_cv; | The member typedef type denotes the type formed  by removing any top-level cv-qualifiers from T[.](#tab:meta.trans.cv-row-4-column-2-sentence-1)<br>[*Example [3](#tab:meta.trans.cv-row-4-column-2-example-3)*:<br>remove_cv_t<const volatile int>  evaluates to int, whereas remove_cv_t<const volatile int*>  evaluates to const volatile int*[.](#tab:meta.trans.cv-row-4-column-2-sentence-2) â *end example*] |
| [ð](#tab:meta.trans.cv-row-5)<br>template<class T>   struct add_const; | The member typedef type denotes const T[.](#tab:meta.trans.cv-row-5-column-2-sentence-1)<br>[*Note [1](#tab:meta.trans.cv-row-5-column-2-note-1)*:<br>const has no effect when T is a reference, function, or top-level const-qualified type[.](#tab:meta.trans.cv-row-5-column-2-sentence-2) â *end note*] |
| [ð](#tab:meta.trans.cv-row-6)<br>template<class T>   struct add_volatile; | The member typedef type denotes volatile T[.](#tab:meta.trans.cv-row-6-column-2-sentence-1)<br>[*Note [2](#tab:meta.trans.cv-row-6-column-2-note-2)*:<br>volatile has no effect when T is a reference, function, or top-level volatile-qualified type[.](#tab:meta.trans.cv-row-6-column-2-sentence-2) â *end note*] |
| [ð](#tab:meta.trans.cv-row-7)<br>template<class T>   struct add_cv; | The member typedef type denotes  add_const_t<add_volatile_t<T>>[.](#tab:meta.trans.cv-row-7-column-2-sentence-1) |

#### [21.3.9.3](#meta.trans.ref) Reference modifications [[meta.trans.ref]](meta.trans.ref)

[1](#meta.trans.ref-1)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L2054)

The templates specified in Table [58](#tab:meta.trans.ref "Table 58: Reference modifications") add or remove references[.](#meta.trans.ref-1.sentence-1)

Table [58](#tab:meta.trans.ref) — Reference modifications [[tab:meta.trans.ref]](./tab:meta.trans.ref)  

| [ð](#tab:meta.trans.ref-row-1)<br>**Template** | **Comments** |
| --- | --- |
| [ð](#tab:meta.trans.ref-row-2)<br>template<class T>   struct remove_reference; | If T has type âreference to T1â then the  member typedef type denotes T1;  otherwise, type denotes T[.](#tab:meta.trans.ref-row-2-column-2-sentence-1) |
| [ð](#tab:meta.trans.ref-row-3)<br>template<class T>   struct add_lvalue_reference; | If T is a referenceable type ([[defns.referenceable]](defns.referenceable "3.45 referenceable type")) then  the member typedef type denotes T&;  otherwise, type denotes T[.](#tab:meta.trans.ref-row-3-column-2-sentence-1)<br>[*Note [1](#tab:meta.trans.ref-row-3-column-2-note-1)*:<br>This rule reflects the semantics of reference collapsing ([[dcl.ref]](dcl.ref "9.3.4.3 References"))[.](#tab:meta.trans.ref-row-3-column-2-sentence-2) â *end note*] |
| [ð](#tab:meta.trans.ref-row-4)<br>template<class T>   struct add_rvalue_reference; | If T is a referenceable type then  the member typedef type denotes T&&;  otherwise, type denotes T[.](#tab:meta.trans.ref-row-4-column-2-sentence-1)<br>[*Note [2](#tab:meta.trans.ref-row-4-column-2-note-2)*:<br>This rule reflects the semantics of reference collapsing ([[dcl.ref]](dcl.ref "9.3.4.3 References"))[.](#tab:meta.trans.ref-row-4-column-2-sentence-2)<br>For example, when a type T is a reference type T1&,  the type add_rvalue_reference_t<T> is not an rvalue reference[.](#tab:meta.trans.ref-row-4-column-2-sentence-3) â *end note*] |

#### [21.3.9.4](#meta.trans.sign) Sign modifications [[meta.trans.sign]](meta.trans.sign)

[1](#meta.trans.sign-1)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L2101)

The templates specified in Table [59](#tab:meta.trans.sign "Table 59: Sign modifications") convert an integer type to its corresponding signed or unsigned type[.](#meta.trans.sign-1.sentence-1)

Table [59](#tab:meta.trans.sign) — Sign modifications [[tab:meta.trans.sign]](./tab:meta.trans.sign)  

| [ð](#tab:meta.trans.sign-row-1)<br>**Template** | **Comments** |
| --- | --- |
| [ð](#tab:meta.trans.sign-row-2)<br>template<class T>   struct make_signed; | If T is a (possibly cv-qualified) signed integer  type ([[basic.fundamental]](basic.fundamental "6.9.2 Fundamental types")) 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]](conv.rank "6.9.6 Conversion ranks")) for which  sizeof(T) == sizeof(type), with the same  cv-qualifiers as T[.](#tab:meta.trans.sign-row-2-column-2-sentence-1)<br>   *Mandates*: T is an integral or enumeration type  other than cv bool[.](#tab:meta.trans.sign-row-2-column-2-sentence-2) |
| [ð](#tab:meta.trans.sign-row-3)<br>template<class T>   struct make_unsigned; | If T is a (possibly cv-qualified) unsigned integer  type ([[basic.fundamental]](basic.fundamental "6.9.2 Fundamental types")) 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]](conv.rank "6.9.6 Conversion ranks")) for which  sizeof(T) == sizeof(type), with the same  cv-qualifiers as T[.](#tab:meta.trans.sign-row-3-column-2-sentence-1)<br>   *Mandates*: T is an integral or enumeration type  other than cv bool[.](#tab:meta.trans.sign-row-3-column-2-sentence-2) |

#### [21.3.9.5](#meta.trans.arr) Array modifications [[meta.trans.arr]](meta.trans.arr)

[1](#meta.trans.arr-1)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L2149)

The templates specified in Table [60](#tab:meta.trans.arr "Table 60: Array modifications") modify array types[.](#meta.trans.arr-1.sentence-1)

Table [60](#tab:meta.trans.arr) — Array modifications [[tab:meta.trans.arr]](./tab:meta.trans.arr)  

| [ð](#tab:meta.trans.arr-row-1)<br>**Template** | **Comments** |
| --- | --- |
| [ð](#tab:meta.trans.arr-row-2)<br>template<class T>   struct remove_extent; | If T is a type âarray of Uâ,  the member typedef type denotes U,  otherwise T[.](#tab:meta.trans.arr-row-2-column-2-sentence-1)<br>[*Note [1](#tab:meta.trans.arr-row-2-column-2-note-1)*:<br>For multidimensional arrays, only the first array dimension is  removed[.](#tab:meta.trans.arr-row-2-column-2-sentence-2)<br>For a type âarray of const Uâ, the resulting type is  const U[.](#tab:meta.trans.arr-row-2-column-2-sentence-3) â *end note*] |
| [ð](#tab:meta.trans.arr-row-3)<br>template<class T>   struct remove_all_extents; | If T is âmultidimensional array of Uâ, the resulting member  typedef type denotes U, otherwise T[.](#tab:meta.trans.arr-row-3-column-2-sentence-1) |

[2](#meta.trans.arr-2)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L2182)

[*Example [1](#meta.trans.arr-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*]

[3](#meta.trans.arr-3)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L2193)

[*Example [2](#meta.trans.arr-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.9.6](#meta.trans.ptr) Pointer modifications [[meta.trans.ptr]](meta.trans.ptr)

[1](#meta.trans.ptr-1)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L2206)

The templates specified in Table [61](#tab:meta.trans.ptr "Table 61: Pointer modifications") add or remove pointers[.](#meta.trans.ptr-1.sentence-1)

Table [61](#tab:meta.trans.ptr) — Pointer modifications [[tab:meta.trans.ptr]](./tab:meta.trans.ptr)  

| [ð](#tab:meta.trans.ptr-row-1)<br>**Template** | **Comments** |
| --- | --- |
| [ð](#tab:meta.trans.ptr-row-2)<br>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[.](#tab:meta.trans.ptr-row-2-column-2-sentence-1) |
| [ð](#tab:meta.trans.ptr-row-3)<br>template<class T>   struct add_pointer; | If T is a referenceable type ([[defns.referenceable]](defns.referenceable "3.45 referenceable type")) or a  cv void type then  the member typedef type denotes  remove_reference_t<T>*;  otherwise, type denotes T[.](#tab:meta.trans.ptr-row-3-column-2-sentence-1) |

#### [21.3.9.7](#meta.trans.other) Other transformations [[meta.trans.other]](meta.trans.other)

[1](#meta.trans.other-1)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L2238)

The templates specified in Table [62](#tab:meta.trans.other "Table 62: Other transformations") perform other modifications of a type[.](#meta.trans.other-1.sentence-1)

Table [62](#tab:meta.trans.other) — Other transformations [[tab:meta.trans.other]](./tab:meta.trans.other)  

| [ð](#tab:meta.trans.other-row-1)<br>**Template** | **Comments** |
| --- | --- |
| [ð](#tab:meta.trans.other-row-2)<br>template<class T>   struct [type_identity](#lib:type_identity "21.3.9.7 Other transformations [meta.trans.other]"); | The member typedef type denotes T[.](#tab:meta.trans.other-row-2-column-2-sentence-1) |
| [ð](#tab:meta.trans.other-row-3)<br>template<class T>   struct [remove_cvref](meta.reflection.traits#lib:remove_cvref "21.4.17 Reflection type traits [meta.reflection.traits]"); | The member typedef type denotes  [remove_cv_t](#lib:remove_cv_t "21.3.3 Header <type_­traits> synopsis [meta.type.synop]")<[remove_reference_t](#lib:remove_reference_t "21.3.3 Header <type_­traits> synopsis [meta.type.synop]")<T>>[.](#tab:meta.trans.other-row-3-column-2-sentence-1) |
| [ð](#tab:meta.trans.other-row-4)<br>template<class T>   struct [decay](meta.reflection.traits#lib:decay "21.4.17 Reflection type traits [meta.reflection.traits]"); | Let U be [remove_reference_t](#lib:remove_reference_t "21.3.3 Header <type_­traits> synopsis [meta.type.synop]")<T>[.](#tab:meta.trans.other-row-4-column-2-sentence-1)<br>If [is_array_v](#lib:is_array_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]")<U> is  true, the member typedef type denotes  [remove_extent_t](#lib:remove_extent_t "21.3.3 Header <type_­traits> synopsis [meta.type.synop]")<U>*[.](#tab:meta.trans.other-row-4-column-2-sentence-2)<br>If [is_function_v](#lib:is_function_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]")<U> is true,  the member typedef type denotes [add_pointer_t](#lib:add_pointer_t "21.3.3 Header <type_­traits> synopsis [meta.type.synop]")<U>[.](#tab:meta.trans.other-row-4-column-2-sentence-3)<br>Otherwise  the member typedef type denotes [remove_cv_t](#lib:remove_cv_t "21.3.3 Header <type_­traits> synopsis [meta.type.synop]")<U>[.](#tab:meta.trans.other-row-4-column-2-sentence-4)<br>[*Note [1](#tab:meta.trans.other-row-4-column-2-note-1)*:<br>This behavior is similar to the lvalue-to-rvalue ([[conv.lval]](conv.lval "7.3.2 Lvalue-to-rvalue conversion")), array-to-pointer ([[conv.array]](conv.array "7.3.3 Array-to-pointer conversion")), and function-to-pointer ([[conv.func]](conv.func "7.3.4 Function-to-pointer conversion")) 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[.](#tab:meta.trans.other-row-4-column-2-sentence-5) â *end note*] |
| [ð](#tab:meta.trans.other-row-5)<br>template<bool B, class T = void> struct [enable_if](#lib:enable_if "21.3.9.7 Other transformations [meta.trans.other]"); | If B is true, the member typedef type  denotes T; otherwise, there shall be no member  type[.](#tab:meta.trans.other-row-5-column-2-sentence-1) |
| [ð](#tab:meta.trans.other-row-6)<br>template<bool B, class T,class F>   struct [conditional](#lib:conditional "21.3.9.7 Other transformations [meta.trans.other]"); | If B is true, the member typedef type denotes T[.](#tab:meta.trans.other-row-6-column-2-sentence-1)<br>If B is false, the member typedef type denotes F[.](#tab:meta.trans.other-row-6-column-2-sentence-2) |
| [ð](#tab:meta.trans.other-row-7)<br>template<class... T> struct common_type; | Unless this trait is specialized,  the member type is defined or omitted as specified below[.](#tab:meta.trans.other-row-7-column-2-sentence-1)<br>If it is omitted, there shall be no member type[.](#tab:meta.trans.other-row-7-column-2-sentence-2)<br>Each type in the template parameter pack T shall be  complete, cv void, or an array of unknown bound[.](#tab:meta.trans.other-row-7-column-2-sentence-3) |
| [ð](#tab:meta.trans.other-row-8)<br>template<class, class,template<class> class,template<class> class>struct[basic_common_reference](refwrap.common.ref#lib:basic_common_reference "22.10.6.8 common_­reference related specializations [refwrap.common.ref]"); | Unless this trait is specialized,  there shall be no member type[.](#tab:meta.trans.other-row-8-column-2-sentence-1) |
| [ð](#tab:meta.trans.other-row-9)<br>template<class... T> struct [common_reference](meta.reflection.traits#lib:common_reference "21.4.17 Reflection type traits [meta.reflection.traits]"); | The member [*typedef-name*](dcl.typedef#nt:typedef-name "9.2.4 The typedef specifier [dcl.typedef]") type is defined or omitted  as specified below[.](#tab:meta.trans.other-row-9-column-2-sentence-1)<br>Each type in the parameter pack T shall  be complete or cv void[.](#tab:meta.trans.other-row-9-column-2-sentence-2) |
| [ð](#tab:meta.trans.other-row-10)<br>template<class T>   struct [underlying_type](meta.reflection.traits#lib:underlying_type "21.4.17 Reflection type traits [meta.reflection.traits]"); | If T is an enumeration type, the member typedef type denotes  the underlying type of T ([[dcl.enum]](dcl.enum "9.8.1 Enumeration declarations"));  otherwise, there is no member type[.](#tab:meta.trans.other-row-10-column-2-sentence-1)<br>   *Mandates*: T is not an incomplete enumeration type[.](#tab:meta.trans.other-row-10-column-2-sentence-2) |
| [ð](#tab:meta.trans.other-row-11)<br>template<class Fn,   class... ArgTypes>   struct [invoke_result](meta.reflection.traits#lib:invoke_result "21.4.17 Reflection type traits [meta.reflection.traits]"); | If the expression *INVOKE*(declval<Fn>(), declval<ArgTypes>()...) ([[func.require]](func.require "22.10.4 Requirements"))  is well-formed when treated as an unevaluated operand ([[expr.context]](expr.context#term.unevaluated.operand "7.2.3 Context dependence")),  the member typedef type denotes the type  decltype(*INVOKE*(declval<Fn>(), declval<ArgTypes>()...));  otherwise, there shall be no member type[.](#tab:meta.trans.other-row-11-column-2-sentence-1)<br>Access checking is  performed as if in a context unrelated to Fn and  ArgTypes[.](#tab:meta.trans.other-row-11-column-2-sentence-2)<br>Only the validity of the immediate context of the  expression is considered[.](#tab:meta.trans.other-row-11-column-2-sentence-3)<br>[*Note [2](#tab:meta.trans.other-row-11-column-2-note-2)*:<br>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[.](#tab:meta.trans.other-row-11-column-2-sentence-4)<br>Such side effects are not in the âimmediate  contextâ and can result in the program being ill-formed[.](#tab:meta.trans.other-row-11-column-2-sentence-5) â *end note*]<br>*Preconditions*: Fn and all types in the template parameter pack ArgTypes  are complete types, cv void, or arrays of  unknown bound[.](#tab:meta.trans.other-row-11-column-2-sentence-6) |
| [ð](#tab:meta.trans.other-row-12)<br>template<class Fn, class Tuple>   struct [apply_result](#lib:apply_result "21.3.9.7 Other transformations [meta.trans.other]"); | If [*tuple-like*](tuple.like#concept:tuple-like "22.4.3 Concept tuple-like [tuple.like]")<Tuple> is true  and the expression  *INVOKE*(declval<Fn>(), *ELEMS-OF*(Tuple)...) ([[func.require]](func.require "22.10.4 Requirements"))  is well-formed  when treated as an unevaluated operand ([[expr.context]](expr.context#term.unevaluated.operand "7.2.3 Context dependence")),  the member typedef type denotes the type  decltype(*INVOKE*(declval<Fn>(), *ELEMS-OF*(Tuple)...));  otherwise, there shall be no member type[.](#tab:meta.trans.other-row-12-column-2-sentence-1)<br>Access checking is performed as if in a context unrelated to Fn  and Tuple[.](#tab:meta.trans.other-row-12-column-2-sentence-2)<br>Only the validity of the immediate context of the expression is considered[.](#tab:meta.trans.other-row-12-column-2-sentence-3)<br>[*Note [3](#tab:meta.trans.other-row-12-column-2-note-3)*:<br>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[.](#tab:meta.trans.other-row-12-column-2-sentence-4)<br>Such side effects are not in the âimmediate contextâ  and can result in the program being ill-formed[.](#tab:meta.trans.other-row-12-column-2-sentence-5) â *end note*]<br>*Preconditions*: Fn and Tuple are complete types, cv void,  or arrays of unknown bound[.](#tab:meta.trans.other-row-12-column-2-sentence-6) |
| [ð](#tab:meta.trans.other-row-13)<br>template<class T> struct [unwrap_reference](meta.reflection.traits#lib:unwrap_reference "21.4.17 Reflection type traits [meta.reflection.traits]"); | If T is  a specialization [reference_wrapper](refwrap.general#lib:reference_wrapper "22.10.6.1 General [refwrap.general]")<X> for some type X,  the member typedef type of unwrap_reference<T>  denotes X&,  otherwise type denotes T[.](#tab:meta.trans.other-row-13-column-2-sentence-1) |
| [ð](#tab:meta.trans.other-row-14)<br>template<class T> [unwrap_ref_decay](functional.syn#lib:unwrap_ref_decay "22.10.2 Header <functional> synopsis [functional.syn]"); | The member typedef type of unwrap_ref_decay<T>  denotes the type [unwrap_reference_t](#lib:unwrap_reference_t "21.3.3 Header <type_­traits> synopsis [meta.type.synop]")<[decay_t](#lib:decay_t "21.3.3 Header <type_­traits> synopsis [meta.type.synop]")<T>>[.](#tab:meta.trans.other-row-14-column-2-sentence-1) |

[2](#meta.trans.other-2)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L2385)

In addition to being available via inclusion
of the <type_traits> header, the templatesunwrap_reference,unwrap_ref_decay,unwrap_reference_t, andunwrap_ref_decay_t are available
when the header <functional> ([[functional.syn]](functional.syn "22.10.2 Header <functional> synopsis")) is included[.](#meta.trans.other-2.sentence-1)

[3](#meta.trans.other-3)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L2396)

Let:

- [(3.1)](#meta.trans.other-3.1)

*CREF*(A) be [add_lvalue_reference_t](#lib:add_lvalue_reference_t "21.3.3 Header <type_­traits> synopsis [meta.type.synop]")<const [remove_reference_t](#lib:remove_reference_t "21.3.3 Header <type_­traits> synopsis [meta.type.synop]")<A>>,

- [(3.2)](#meta.trans.other-3.2)

*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,

- [(3.3)](#meta.trans.other-3.3)

*COPYCV*(FROM, TO) be an alias for type TO with the addition of FROM's top-level cv-qualifiers,
  [*Example [1](#meta.trans.other-example-1)*:
  *COPYCV*(const int, volatile short) is an alias for const volatile short[.](#meta.trans.other-3.3.sentence-1)
 â *end example*]

- [(3.4)](#meta.trans.other-3.4)

*COND-RES*(X, Y) be decltype(false ? declval<X(&)()>()() : declval<Y(&)()>()())[.](#meta.trans.other-3.sentence-1)

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:

- [(3.5)](#meta.trans.other-3.5)

  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[.](#meta.trans.other-3.5.sentence-1)

- [(3.6)](#meta.trans.other-3.6)

  Otherwise, let C be [remove_reference_t](#lib:remove_reference_t "21.3.3 Header <type_­traits> synopsis [meta.type.synop]")<*COMMON-REF*(X&, Y&)>&&[.](#meta.trans.other-3.6.sentence-1)
  If A and B are both rvalue reference types, C is well-formed, and [is_convertible_v](#lib:is_convertible_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]")<A, C> && [is_convertible_v](#lib:is_convertible_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]")<B, C> is true,
 then *COMMON-REF*(A, B) is C[.](#meta.trans.other-3.6.sentence-2)

- [(3.7)](#meta.trans.other-3.7)

  Otherwise, let D be *COMMON-REF*(const X&, Y&)[.](#meta.trans.other-3.7.sentence-1)
  If A is an rvalue
 reference and B is an lvalue reference and D is
 well-formed and [is_convertible_v](#lib:is_convertible_v "21.3.3 Header <type_­traits> synopsis [meta.type.synop]")<A, D> is true, then *COMMON-REF*(A, B) is D[.](#meta.trans.other-3.7.sentence-2)

- [(3.8)](#meta.trans.other-3.8)

  Otherwise, if A is an lvalue reference and B is an rvalue reference, then *COMMON-REF*(A, B) is *COMMON-REF*(B, A)[.](#meta.trans.other-3.8.sentence-1)

- [(3.9)](#meta.trans.other-3.9)

  Otherwise, *COMMON-REF*(A, B) is ill-formed[.](#meta.trans.other-3.9.sentence-1)

If any of the types computed above is ill-formed, then*COMMON-REF*(A, B) is ill-formed[.](#meta.trans.other-3.sentence-3)

[4](#meta.trans.other-4)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L2444)

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:

- [(4.1)](#meta.trans.other-4.1)

  If sizeof...(T) is zero, there shall be no member type[.](#meta.trans.other-4.1.sentence-1)

- [(4.2)](#meta.trans.other-4.2)

  If sizeof...(T) is one, let T0 denote the sole type
constituting the pack T[.](#meta.trans.other-4.2.sentence-1)
  The member [*typedef-name*](dcl.typedef#nt:typedef-name "9.2.4 The typedef specifier [dcl.typedef]") type shall denote the same
type, if any, as common_type_t<T0, T0>;
otherwise there shall be no member type[.](#meta.trans.other-4.2.sentence-2)

- [(4.3)](#meta.trans.other-4.3)

  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[.](#meta.trans.other-4.3.sentence-1)

  * [(4.3.1)](#meta.trans.other-4.3.1)

      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>[.](#meta.trans.other-4.3.1.sentence-1)

  * [(4.3.2)](#meta.trans.other-4.3.2)

      [*Note [4](#meta.trans.other-note-4)*:
      None of the following will apply if there is a specialization common_type<D1, D2>[.](#meta.trans.other-4.3.2.sentence-1)
 â *end note*]

  * [(4.3.3)](#meta.trans.other-4.3.3)

      Otherwise, ifdecay_t<decltype(false ? declval<D1>() : declval<D2>())> denotes a valid type, let C denote that type[.](#meta.trans.other-4.3.3.sentence-1)

  * [(4.3.4)](#meta.trans.other-4.3.4)

      Otherwise, if *COND-RES*(*CREF*(D1), *CREF*(D2)) denotes a type, let C denote the type decay_t<*COND-RES*(*CREF*(D1), *CREF*(D2))>[.](#meta.trans.other-4.3.4.sentence-1)

   In either case, the member [*typedef-name*](dcl.typedef#nt:typedef-name "9.2.4 The typedef specifier [dcl.typedef]") type shall denote
the same type, if any, as C[.](#meta.trans.other-4.3.sentence-2)
  Otherwise, there shall be no member type[.](#meta.trans.other-4.3.sentence-3)

- [(4.4)](#meta.trans.other-4.4)

  If sizeof...(T) is greater than two,
let T1, T2, and R, respectively,
denote the first, second, and (pack of) remaining types constituting T[.](#meta.trans.other-4.4.sentence-1)
  Let C denote the same type, if any, as common_type_t<T1, T2>[.](#meta.trans.other-4.4.sentence-2)
  If there is such a type C, the member [*typedef-name*](dcl.typedef#nt:typedef-name "9.2.4 The typedef specifier [dcl.typedef]") type shall denote the same type, if any, as common_type_t<C, R...>[.](#meta.trans.other-4.4.sentence-3)
  Otherwise, there shall be no member type[.](#meta.trans.other-4.4.sentence-4)

[5](#meta.trans.other-5)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L2496)

Notwithstanding the provisions of [[meta.rqmts]](#meta.rqmts "21.3.2 Requirements"), and
pursuant to [[namespace.std]](namespace.std "16.4.5.2.1 Namespace std"),
a program may specialize common_type<T1, T2> for types T1 and T2 such thatis_same_v<T1, decay_t<T1>> andis_same_v<T2, decay_t<T2>> are each true[.](#meta.trans.other-5.sentence-1)

[*Note [5](#meta.trans.other-note-5)*:

Such specializations are needed when only explicit conversions
are desired between the template arguments[.](#meta.trans.other-5.sentence-2)

â *end note*]

Such a specialization need not have a member named type,
but if it does,
the [*qualified-id*](expr.prim.id.qual#nt:qualified-id "7.5.5.3 Qualified names [expr.prim.id.qual]") 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[.](#meta.trans.other-5.sentence-3)

Moreover, common_type_t<T1, T2> shall denote
the same type, if any, as does common_type_t<T2, T1>[.](#meta.trans.other-5.sentence-4)

No diagnostic is required for a violation of this Note's rules[.](#meta.trans.other-5.sentence-5)

[6](#meta.trans.other-6)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L2516)

For the common_reference trait applied to a parameter packT of types, the member type shall be either defined or not
present as follows:

- [(6.1)](#meta.trans.other-6.1)

  If sizeof...(T) is zero, there shall be no member type[.](#meta.trans.other-6.1.sentence-1)

- [(6.2)](#meta.trans.other-6.2)

  Otherwise, if sizeof...(T) is one, let T0 denote the sole
 type in the pack T[.](#meta.trans.other-6.2.sentence-1)
  The member typedef type shall denote the
 same type as T0[.](#meta.trans.other-6.2.sentence-2)

- [(6.3)](#meta.trans.other-6.3)

  Otherwise, if sizeof...(T) is two, let T1 and T2 denote the two types in the pack T[.](#meta.trans.other-6.3.sentence-1)
  Then
  * [(6.3.1)](#meta.trans.other-6.3.1)

      Let R be *COMMON-REF*(T1, T2)[.](#meta.trans.other-6.3.1.sentence-1)
      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[.](#meta.trans.other-6.3.1.sentence-2)

  * [(6.3.2)](#meta.trans.other-6.3.2)

      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[.](#meta.trans.other-6.3.2.sentence-1)

  * [(6.3.3)](#meta.trans.other-6.3.3)

      Otherwise, if *COND-RES*(T1, T2) is well-formed,
 then the member typedef type denotes that type[.](#meta.trans.other-6.3.3.sentence-1)

  * [(6.3.4)](#meta.trans.other-6.3.4)

      Otherwise, if common_type_t<T1, T2> is well-formed, then the
 member typedef type denotes that type[.](#meta.trans.other-6.3.4.sentence-1)

  * [(6.3.5)](#meta.trans.other-6.3.5)

      Otherwise, there shall be no member type[.](#meta.trans.other-6.3.5.sentence-1)

- [(6.4)](#meta.trans.other-6.4)

  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[.](#meta.trans.other-6.4.sentence-1)
  Let C be the type common_reference_t<T1, T2>[.](#meta.trans.other-6.4.sentence-2)
  Then:
  * [(6.4.1)](#meta.trans.other-6.4.1)

      If there is such a type C, the member typedef type shall
 denote the same type, if any, as common_reference_t<C, Rest...>[.](#meta.trans.other-6.4.1.sentence-1)

  * [(6.4.2)](#meta.trans.other-6.4.2)

      Otherwise, there shall be no member type[.](#meta.trans.other-6.4.2.sentence-1)

[7](#meta.trans.other-7)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L2562)

Notwithstanding the provisions of [[meta.rqmts]](#meta.rqmts "21.3.2 Requirements"), and
pursuant to [[namespace.std]](namespace.std "16.4.5.2.1 Namespace std"), a program may partially specializebasic_common_reference<T, U, TQual, UQual> for types T and U such thatis_same_v<T, decay_t<T>> andis_same_v<U, decay_t<U>> are each true[.](#meta.trans.other-7.sentence-1)

[*Note [6](#meta.trans.other-note-6)*:

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[.](#meta.trans.other-7.sentence-2)

â *end note*]

Such a specialization need not have a member named type, but if it does,
the [*qualified-id*](expr.prim.id.qual#nt:qualified-id "7.5.5.3 Qualified names [expr.prim.id.qual]")basic_common_reference<T, U, TQual, UQual>​::​type shall denote a type
to which each of the types TQual<T> andUQual<U> is convertible[.](#meta.trans.other-7.sentence-3)

Moreover, basic_common_reference<T, U, TQual, UQual>​::​type shall denote
the same type, if any, as doesbasic_common_reference<U, T, UQual, TQual>​::​type[.](#meta.trans.other-7.sentence-4)

No diagnostic is required for a violation of these rules[.](#meta.trans.other-7.sentence-5)

[8](#meta.trans.other-8)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L2586)

[*Example [2](#meta.trans.other-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.10](#meta.logical) Logical operator traits [[meta.logical]](meta.logical)

[1](#meta.logical-1)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L2616)

This subclause describes type traits for applying logical operators
to other type traits[.](#meta.logical-1.sentence-1)

[ð](#lib:conjunction)

`template<class... B> struct conjunction : see below { };
`

[2](#meta.logical-2)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L2626)

The class template conjunction forms the logical conjunction of its template type arguments[.](#meta.logical-2.sentence-1)

[3](#meta.logical-3)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L2630)

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[.](#meta.logical-3.sentence-1)

[*Note [1](#meta.logical-note-1)*:

This is analogous to the short-circuiting behavior of
the built-in operator &&[.](#meta.logical-3.sentence-2)

â *end note*]

[4](#meta.logical-4)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L2641)

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[.](#meta.logical-4.sentence-1)

[5](#meta.logical-5)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L2650)

The specialization conjunction<B1, …, BN> has a public and unambiguous base that is either

- [(5.1)](#meta.logical-5.1)

the first type Bi in the list true_type, B1, …, BN for which bool(Bi​::​value) is false, or

- [(5.2)](#meta.logical-5.2)

if there is no such Bi, the last type in the list[.](#meta.logical-5.sentence-1)

[*Note [2](#meta.logical-note-2)*:

This means a specialization of conjunction does not necessarily inherit from
either true_type or false_type[.](#meta.logical-5.sentence-2)

â *end note*]

[6](#meta.logical-6)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L2666)

The member names of the base class, other than conjunction andoperator=, shall not be hidden and shall be unambiguously available
in conjunction[.](#meta.logical-6.sentence-1)

[ð](#lib:disjunction)

`template<class... B> struct disjunction : see below { };
`

[7](#meta.logical-7)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L2678)

The class template disjunction forms the logical disjunction of its template type arguments[.](#meta.logical-7.sentence-1)

[8](#meta.logical-8)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L2682)

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[.](#meta.logical-8.sentence-1)

[*Note [3](#meta.logical-note-3)*:

This is analogous to the short-circuiting behavior of
the built-in operator ||[.](#meta.logical-8.sentence-2)

â *end note*]

[9](#meta.logical-9)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L2693)

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[.](#meta.logical-9.sentence-1)

[10](#meta.logical-10)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L2702)

The specialization disjunction<B1, …, BN> has a public and unambiguous base that is either

- [(10.1)](#meta.logical-10.1)

the first type Bi in the list false_type, B1, …, BN for which bool(Bi​::​value) is true, or

- [(10.2)](#meta.logical-10.2)

if there is no such Bi, the last type in the list[.](#meta.logical-10.sentence-1)

[*Note [4](#meta.logical-note-4)*:

This means a specialization of disjunction does not necessarily inherit from
either true_type or false_type[.](#meta.logical-10.sentence-2)

â *end note*]

[11](#meta.logical-11)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L2716)

The member names of the base class,
other than disjunction and operator=,
shall not be hidden and shall be unambiguously available in disjunction[.](#meta.logical-11.sentence-1)

[ð](#lib:negation)

`template<class B> struct negation : see below { };
`

[12](#meta.logical-12)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L2728)

The class template negation forms the logical negation of its template type argument[.](#meta.logical-12.sentence-1)

The type negation<B> is a [*Cpp17UnaryTypeTrait*](#:Cpp17UnaryTypeTrait "21.3.2 Requirements [meta.rqmts]") with a base characteristic of bool_constant<!bool(B​::​​value)>[.](#meta.logical-12.sentence-2)

### [21.3.11](#meta.member) Member relationships [[meta.member]](meta.member)

[ð](#lib:is_pointer_interconvertible_with_class)

`template<class S, class M>
  constexpr bool is_pointer_interconvertible_with_class(M S::*m) noexcept;
`

[1](#meta.member-1)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L2744)

*Mandates*: S is a complete type[.](#meta.member-1.sentence-1)

[2](#meta.member-2)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L2748)

*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]](basic.compound "6.9.4 Compound types"))
 with its subobject s.*m[.](#meta.member-2.sentence-1)

[ð](#lib:is_corresponding_member)

`template<class S1, class S2, class M1, class M2>
  constexpr bool is_corresponding_member(M1 S1::*m1, M2 S2::*m2) noexcept;
`

[3](#meta.member-3)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L2766)

*Mandates*: S1 and S2 are complete types[.](#meta.member-3.sentence-1)

[4](#meta.member-4)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L2770)

*Returns*: true if and only if S1 and S2 are standard-layout struct ([[class.prop]](class.prop "11.2 Properties of classes")) 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]](class.mem "11.4 Class members")) of S1 and S2[.](#meta.member-4.sentence-1)

[5](#meta.member-5)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L2780)

[*Note [1](#meta.member-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[.](#meta.member-5.sentence-1)

[*Example [1](#meta.member-example-1)*: struct A { int a; }; // a standard-layout classstruct B { int b; }; // a standard-layout classstruct C: public A, public B { }; // not a standard-layout classstatic_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.12](#meta.const.eval) Constant evaluation context [[meta.const.eval]](meta.const.eval)

[ð](#lib:is_constant_evaluated)

`constexpr bool is_constant_evaluated() noexcept;
`

[1](#meta.const.eval-1)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L2816)

*Effects*: Equivalent to:if consteval {return true;} else {return false;}

[2](#meta.const.eval-2)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L2827)

[*Example [1](#meta.const.eval-example-1)*: constexpr void f(unsigned char *p, int n) {if (std::is_constant_evaluated()) { // should not be a constexpr if statementfor (int k = 0; k<n; ++k) p[k] = 0; } else { memset(p, 0, n); // not a core constant expression}} â *end example*]

[ð](#lib:is_within_lifetime)

`consteval bool is_within_lifetime(const auto* p) noexcept;
`

[3](#meta.const.eval-3)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L2847)

*Returns*: true if p is a pointer to an object that is
within its lifetime ([[basic.life]](basic.life "6.8.4 Lifetime")); otherwise, false[.](#meta.const.eval-3.sentence-1)

[4](#meta.const.eval-4)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L2852)

*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[.](#meta.const.eval-4.sentence-1)

[5](#meta.const.eval-5)

[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/meta.tex#L2860)

[*Example [2](#meta.const.eval-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 2constexpr 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*]
