17 Concepts library [concepts]

17.4 Language-related concepts [concepts.lang]

17.4.1 General [concepts.lang.general]

This subclause contains the definition of concepts corresponding to language features.
These concepts express relationships between types, type classifications, and fundamental type properties.

17.4.2 Concept Same [concept.same]

template<class T, class U> concept Same = is_same_v<T, U>;
Same<T, U> subsumes Same<U, T> and vice versa.

17.4.3 Concept DerivedFrom [concept.derivedfrom]

template<class Derived, class Base> concept DerivedFrom = is_base_of_v<Base, Derived> && is_convertible_v<const volatile Derived*, const volatile Base*>;
[ Note
:
DerivedFrom<Derived, Base> is satisfied if and only if Derived is publicly and unambiguously derived from Base, or Derived and Base are the same class type ignoring cv-qualifiers.
— end note
 ]

17.4.4 Concept ConvertibleTo [concept.convertibleto]

The ConvertibleTo concept requires an expression of a particular type and value category to be both implicitly and explicitly convertible to some other type.
The implicit and explicit conversions are required to produce equal results.
template<class From, class To> concept ConvertibleTo = is_convertible_v<From, To> && requires(From (&f)()) { static_cast<To>(f()); };
Let test be the invented function:
To test(From (&f)()) {
  return f();
}
and let f be a function with no arguments and return type From such that f() is equality-preserving.
ConvertibleTo<From, To> is satisfied only if:
  • To is not an object or reference-to-object type, or static_­cast<To>(f()) is equal to test(f).
  • From is not a reference-to-object type, or
    • If From is an rvalue reference to a non const-qualified type, the resulting state of the object referenced by f() after either above expression is valid but unspecified ([lib.types.movedfrom]).
    • Otherwise, the object referred to by f() is not modified by either above expression.

17.4.5 Concept CommonReference [concept.commonref]

For two types T and U, if common_­reference_­t<T, U> is well-formed and denotes a type C such that both ConvertibleTo<T, C> and ConvertibleTo<U, C> are satisfied, then T and U share a common reference type, C.
[ Note
:
C could be the same as T, or U, or it could be a different type.
C may be a reference type.
— end note
 ]
template<class T, class U> concept CommonReference = Same<common_reference_t<T, U>, common_reference_t<U, T>> && ConvertibleTo<T, common_reference_t<T, U>> && ConvertibleTo<U, common_reference_t<T, U>>;
Let C be common_­reference_­t<T, U>.
Let t be a function whose return type is T, and let u be a function whose return type is U.
CommonReference<T, U> is satisfied only if:
  • C(t()) equals C(t()) if and only if t() is an equality-preserving expression ([concepts.equality]).
  • C(u()) equals C(u()) if and only if u() is an equality-preserving expression.
[ Note
:
Users can customize the behavior of CommonReference by specializing the basic_­common_­reference class template ([meta.trans.other]).
— end note
 ]

17.4.6 Concept Common [concept.common]

If T and U can both be explicitly converted to some third type, C, then T and U share a common type, C.
[ Note
:
C could be the same as T, or U, or it could be a different type.
C might not be unique.
— end note
 ]
template<class T, class U> concept Common = Same<common_type_t<T, U>, common_type_t<U, T>> && ConvertibleTo<T, common_type_t<T, U>> && ConvertibleTo<U, common_type_t<T, U>> && CommonReference< add_lvalue_reference_t<const T>, add_lvalue_reference_t<const U>> && CommonReference< add_lvalue_reference_t<common_type_t<T, U>>, common_reference_t< add_lvalue_reference_t<const T>, add_lvalue_reference_t<const U>>>;
Let C be common_­type_­t<T, U>.
Let t be a function whose return type is T, and let u be a function whose return type is U.
Common<T, U> is satisfied only if:
[ Note
:
Users can customize the behavior of Common by specializing the common_­type class template ([meta.trans.other]).
— end note
 ]

17.4.7 Integral concepts [concepts.integral]

template<class T> concept Integral = is_integral_v<T>; template<class T> concept SignedIntegral = Integral<T> && is_signed_v<T>; template<class T> concept UnsignedIntegral = Integral<T> && !SignedIntegral<T>;
[ Note
:
SignedIntegral<T> can be satisfied even by types that are not signed integral types ([basic.fundamental]); for example, char.
— end note
 ]
[ Note
:
UnsignedIntegral<T> can be satisfied even by types that are not unsigned integral types ([basic.fundamental]); for example, bool.
— end note
 ]

17.4.8 Concept Assignable [concept.assignable]

template<class LHS, class RHS> concept Assignable = is_lvalue_reference_v<LHS> && CommonReference<const remove_reference_t<LHS>&, const remove_reference_t<RHS>&> && requires(LHS lhs, RHS&& rhs) { lhs = std::forward<RHS>(rhs); requires Same<decltype(lhs = std::forward<RHS>(rhs)), LHS>; };
Let:
  • lhs be an lvalue that refers to an object lcopy such that decltype((lhs)) is LHS,
  • rhs be an expression such that decltype((rhs)) is RHS, and
  • rcopy be a distinct object that is equal to rhs.
Assignable<LHS, RHS> is satisfied only if
  • addressof(lhs = rhs) == addressof(lcopy).
  • After evaluating lhs = rhs:
    • lhs is equal to rcopy, unless rhs is a non-const xvalue that refers to lcopy.
    • If rhs is a non-const xvalue, the resulting state of the object to which it refers is valid but unspecified ([lib.types.movedfrom]).
    • Otherwise, if rhs is a glvalue, the object to which it refers is not modified.
[ Note
:
Assignment need not be a total function ([structure.requirements]); in particular, if assignment to an object x can result in a modification of some other object y, then x = y is likely not in the domain of =.
— end note
 ]

17.4.9 Concept Swappable [concept.swappable]

template<class T> concept Swappable = is_swappable_v<T>;
Let a1 and a2 denote distinct equal objects of type T, and let b1 and b2 similarly denote distinct equal objects of type T.
Swappable<T> is satisfied only if after evaluating either swap(a1, b1) or swap(b1, a1) in the context described below, a1 is equal to b2 and b1 is equal to a2.
The context in which swap(a1, b1) or swap(b1, a1) is evaluated shall ensure that a binary non-member function named swap is selected via overload resolution ([over.match]) on a candidate set that includes:
template<class T, class U> concept SwappableWith = is_swappable_with_v<T, T> && is_swappable_with_v<U, U> && CommonReference<const remove_reference_t<T>&, const remove_reference_t<U>&> && is_swappable_with_v<T, U> && is_swappable_with_v<U, T>;
Let:
  • t1 and t2 denote distinct equal objects of type remove_­cvref_­t<T>,
  • be an expression that denotes t1 such that decltype(()) is T,
  • u1 and u2 similarly denote distinct equal objects of type remove_­cvref_­t<U>,
  • be an expression that denotes u1 such that decltype(()) is U, and
  • C be
        common_reference_t<const remove_reference_t<T>&, const remove_reference_t<U>&>
SwappableWith<T, U> is satisfied only if after evaluating either swap(, ) or swap(, ) in the context described above, C(t1) is equal to C(u2) and C(u1) is equal to C(t2).
The context in which swap(, ) or swap(, ) is evaluated shall ensure that a binary non-member function named swap is selected via overload resolution ([over.match]) on a candidate set that includes:
[ Example
:
User code can ensure that the evaluation of swap calls is performed in an appropriate context under the various conditions as follows:
#include <cassert>
#include <concepts>
#include <utility>

template<class T, std::SwappableWith<T> U>
void value_swap(T&& t, U&& u) {
  using std::swap;
  swap(std::forward<T>(t), std::forward<U>(u)); // OK: uses β€œswappable with” conditions
                                                // for rvalues and lvalues
}

template<std::Swappable T>
void lv_swap(T& t1, T& t2) {
  using std::swap;
  swap(t1, t2);                                 // OK: uses swappable conditions for
}                                               // lvalues of type T

namespace N {
  struct A { int m; };
  struct Proxy { A* a; };
  Proxy proxy(A& a) { return Proxy{ &a }; }

  void swap(A& x, Proxy p) {
    std::swap(x.m, p.a->m);                     // OK: uses context equivalent to swappable
                                                // conditions for fundamental types
  }
  void swap(Proxy p, A& x) { swap(x, p); }      // satisfy symmetry constraint
}

int main() {
  int i = 1, j = 2;
  lv_swap(i, j);
  assert(i == 2 && j == 1);

  N::A a1 = { 5 }, a2 = { -5 };
  value_swap(a1, proxy(a2));
  assert(a1.m == -5 && a2.m == 5);
}
— end example
 ]

17.4.10 Concept Destructible [concept.destructible]

The Destructible concept specifies properties of all types, instances of which can be destroyed at the end of their lifetime, or reference types.
template<class T> concept Destructible = is_nothrow_destructible_v<T>;
[ Note
:
Unlike the Cpp17Destructible requirements (Table 29), this concept forbids destructors that are potentially throwing, even if a particular invocation of the destructor does not actually throw.
— end note
 ]

17.4.11 Concept Constructible [concept.constructible]

The Constructible concept constrains the initialization of a variable of a given type with a particular set of argument types.
template<class T, class... Args> concept Constructible = Destructible<T> && is_constructible_v<T, Args...>;

17.4.12 Concept DefaultConstructible [concept.defaultconstructible]

template<class T> concept DefaultConstructible = Constructible<T>;

17.4.13 Concept MoveConstructible [concept.moveconstructible]

template<class T> concept MoveConstructible = Constructible<T, T> && ConvertibleTo<T, T>;
If T is an object type, then let rv be an rvalue of type T and u2 a distinct object of type T equal to rv.
MoveConstructible<T> is satisfied only if
  • After the definition T u = rv;, u is equal to u2.
  • T(rv) is equal to u2.
  • If T is not const, rv's resulting state is valid but unspecified ([lib.types.movedfrom]); otherwise, it is unchanged.

17.4.14 Concept CopyConstructible [concept.copyconstructible]

template<class T> concept CopyConstructible = MoveConstructible<T> && Constructible<T, T&> && ConvertibleTo<T&, T> && Constructible<T, const T&> && ConvertibleTo<const T&, T> && Constructible<T, const T> && ConvertibleTo<const T, T>;
If T is an object type, then let v be an lvalue of type (possibly const) T or an rvalue of type const T.
CopyConstructible<T> is satisfied only if
  • After the definition T u = v;, u is equal to v.
  • T(v) is equal to v.