18 Concepts library [concepts]

18.1 General [concepts.general]

This Clause describes library components that C++ programs may use to perform compile-time validation of template arguments and perform function dispatch based on properties of types.
The purpose of these concepts is to establish a foundation for equational reasoning in programs.
The following subclauses describe language-related concepts, comparison concepts, object concepts, and callable concepts as summarized in Table 39.
Table 39: Fundamental concepts library summary [tab:concepts.summary]
Subclause
Header
Equality preservation
Language-related concepts
<concepts>
Comparison concepts
Object concepts
Callable concepts

18.2 Equality preservation [concepts.equality]

An expression is equality-preserving if, given equal inputs, the expression results in equal outputs.
The inputs to an expression are the set of the expression's operands.
The output of an expression is the expression's result and all operands modified by the expression.
For the purposes of this subclause, the operands of an expression are the largest subexpressions that include only:
[Example 1:
The operands of the expression a = std​::​move(b) are a and std​::​move(b).
— end example]
Not all input values need be valid for a given expression; e.g., for integers a and b, the expression a / b is not well-defined when b is 0.
This does not preclude the expression a / b being equality-preserving.
The domain of an expression is the set of input values for which the expression is required to be well-defined.
Expressions required to be equality-preserving are further required to be stable: two evaluations of such an expression with the same input objects are required to have equal outputs absent any explicit intervening modification of those input objects.
[Note 1:
This requirement allows generic code to reason about the current values of objects based on knowledge of the prior values as observed via equality-preserving expressions.
It effectively forbids spontaneous changes to an object, changes to an object from another thread of execution, changes to an object as side effects of non-modifying expressions, and changes to an object as side effects of modifying a distinct object if those changes could be observable to a library function via an equality-preserving expression that is required to be valid for that object.
— end note]
Expressions declared in a requires-expression in the library clauses are required to be equality-preserving, except for those annotated with the comment “not required to be equality-preserving.
An expression so annotated may be equality-preserving, but is not required to be so.
An expression that may alter the value of one or more of its inputs in a manner observable to equality-preserving expressions is said to modify those inputs.
The library clauses use a notational convention to specify which expressions declared in a requires-expression modify which inputs: except where otherwise specified, an expression operand that is a non-constant lvalue or rvalue may be modified.
Operands that are constant lvalues or rvalues are required to not be modified.
For the purposes of this subclause, the cv-qualification and value category of each operand are determined by assuming that each template type parameter denotes a cv-unqualified complete non-array object type.
Where a requires-expression declares an expression that is non-modifying for some constant lvalue operand, additional variations of that expression that accept a non-constant lvalue or (possibly constant) rvalue for the given operand are also required except where such an expression variation is explicitly required with differing semantics.
These implicit expression variations are required to meet the semantic requirements of the declared expression.
The extent to which an implementation validates the syntax of the variations is unspecified.
[Example 2: template<class T> concept C = requires(T a, T b, const T c, const T d) { c == d; // #1 a = std::move(b); // #2 a = c; // #3 };
For the above example:
  • Expression #1 does not modify either of its operands, #2 modifies both of its operands, and #3 modifies only its first operand a.
  • Expression #1 implicitly requires additional expression variations that meet the requirements for c == d (including non-modification), as if the expressions c == b; c == std::move(d); c == std::move(b); std::move(c) == d; std::move(c) == b; std::move(c) == std::move(d); std::move(c) == std::move(b); a == d; a == b; a == std::move(d); a == std::move(b); std::move(a) == d; std::move(a) == b; std::move(a) == std::move(d); std::move(a) == std::move(b); had been declared as well.
  • Expression #3 implicitly requires additional expression variations that meet the requirements for a = c (including non-modification of the second operand), as if the expressions a = b and a = std​::​move(c) had been declared.
    Expression #3 does not implicitly require an expression variation with a non-constant rvalue second operand, since expression #2 already specifies exactly such an expression explicitly.
— end example]
[Example 3:
The following type T meets the explicitly stated syntactic requirements of concept C above but does not meet the additional implicit requirements: struct T { bool operator==(const T&) const { return true; } bool operator==(T&) = delete; };
T fails to meet the implicit requirements of C, so T satisfies but does not model C.
Since implementations are not required to validate the syntax of implicit requirements, it is unspecified whether an implementation diagnoses as ill-formed a program that requires C<T>.
— end example]

18.3 Header <concepts> synopsis [concepts.syn]

namespace std { // [concepts.lang], language-related concepts // [concept.same], concept same_­as template<class T, class U> concept same_as = see below; // [concept.derived], concept derived_­from template<class Derived, class Base> concept derived_from = see below; // [concept.convertible], concept convertible_­to template<class From, class To> concept convertible_to = see below; // [concept.commonref], concept common_­reference_­with template<class T, class U> concept common_reference_with = see below; // [concept.common], concept common_­with template<class T, class U> concept common_with = see below; // [concepts.arithmetic], arithmetic concepts template<class T> concept integral = see below; template<class T> concept signed_integral = see below; template<class T> concept unsigned_integral = see below; template<class T> concept floating_point = see below; // [concept.assignable], concept assignable_­from template<class LHS, class RHS> concept assignable_from = see below; // [concept.swappable], concept swappable namespace ranges { inline namespace unspecified { inline constexpr unspecified swap = unspecified; } } template<class T> concept swappable = see below; template<class T, class U> concept swappable_with = see below; // [concept.destructible], concept destructible template<class T> concept destructible = see below; // [concept.constructible], concept constructible_­from template<class T, class... Args> concept constructible_from = see below; // [concept.default.init], concept default_­initializable template<class T> concept default_initializable = see below; // [concept.moveconstructible], concept move_­constructible template<class T> concept move_constructible = see below; // [concept.copyconstructible], concept copy_­constructible template<class T> concept copy_constructible = see below; // [concepts.compare], comparison concepts // [concept.equalitycomparable], concept equality_­comparable template<class T> concept equality_comparable = see below; template<class T, class U> concept equality_comparable_with = see below; // [concept.totallyordered], concept totally_­ordered template<class T> concept totally_ordered = see below; template<class T, class U> concept totally_ordered_with = see below; // [concepts.object], object concepts template<class T> concept movable = see below; template<class T> concept copyable = see below; template<class T> concept semiregular = see below; template<class T> concept regular = see below; // [concepts.callable], callable concepts // [concept.invocable], concept invocable template<class F, class... Args> concept invocable = see below; // [concept.regularinvocable], concept regular_­invocable template<class F, class... Args> concept regular_invocable = see below; // [concept.predicate], concept predicate template<class F, class... Args> concept predicate = see below; // [concept.relation], concept relation template<class R, class T, class U> concept relation = see below; // [concept.equiv], concept equivalence_­relation template<class R, class T, class U> concept equivalence_relation = see below; // [concept.strictweakorder], concept strict_­weak_­order template<class R, class T, class U> concept strict_weak_order = see below; }

18.4 Language-related concepts [concepts.lang]

18.4.1 General [concepts.lang.general]

Subclause [concepts.lang] contains the definition of concepts corresponding to language features.
These concepts express relationships between types, type classifications, and fundamental type properties.

18.4.2 Concept same_­as [concept.same]

template<class T, class U> concept same-as-impl = is_­same_­v<T, U>; // exposition only template<class T, class U> concept same_­as = same-as-impl<T, U> && same-as-impl<U, T>;
[Note 1:
same_­as<T, U> subsumes same_­as<U, T> and vice versa.
— end note]

18.4.3 Concept derived_­from [concept.derived]

template<class Derived, class Base> concept derived_­from = is_base_of_v<Base, Derived> && is_convertible_v<const volatile Derived*, const volatile Base*>;
[Note 1:
derived_­from<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]

18.4.4 Concept convertible_­to [concept.convertible]

Given types From and To and an expression E such that decltype((E)) is add_­rvalue_­reference_­t<From>, convertible_­to<From, To> requires E to be both implicitly and explicitly convertible to type To.
The implicit and explicit conversions are required to produce equal results.
template<class From, class To> concept convertible_­to = is_convertible_v<From, To> && requires(add_rvalue_reference_t<From> (&f)()) { static_cast<To>(f()); };
Let FromR be add_­rvalue_­reference_­t<From> and test be the invented function: To test(FromR (&f)()) { return f(); } and let f be a function with no arguments and return type FromR such that f() is equality-preserving.
Types From and To model convertible_­to<From, To> only if:
  • To is not an object or reference-to-object type, or static_­cast<To>(f()) is equal to test(f).
  • FromR is not a reference-to-object type, or
    • If FromR 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.

18.4.5 Concept common_­reference_­with [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 convertible_­to<T, C> and convertible_­to<U, C> are modeled, then T and U share a common reference type, C.
[Note 1:
C could be the same as T, or U, or it could be a different type.
C can be a reference type.
— end note]
template<class T, class U> concept common_­reference_­with = same_­as<common_reference_t<T, U>, common_reference_t<U, T>> && convertible_­to<T, common_reference_t<T, U>> && convertible_­to<U, common_reference_t<T, U>>;
Let C be common_­reference_­t<T, U>.
Let t1 and t2 be equality-preserving expressions ([concepts.equality]) such that decltype((t1)) and decltype((t2)) are each T, and let u1 and u2 be equality-preserving expressions such that decltype((u1)) and decltype((u2)) are each U.
T and U model common_­reference_­with<T, U> only if:
  • C(t1) equals C(t2) if and only if t1 equals t2, and
  • C(u1) equals C(u2) if and only if u1 equals u2.
[Note 2:
Users can customize the behavior of common_­reference_­with by specializing the basic_­common_­reference class template ([meta.trans.other]).
— end note]

18.4.6 Concept common_­with [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 1:
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_­with = same_­as<common_type_t<T, U>, common_type_t<U, T>> && requires { static_cast<common_type_t<T, U>>(declval<T>()); static_cast<common_type_t<T, U>>(declval<U>()); } && common_reference_with< add_lvalue_reference_t<const T>, add_lvalue_reference_t<const U>> && common_reference_with< 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 t1 and t2 be equality-preserving expressions ([concepts.equality]) such that decltype((t1)) and decltype((t2)) are each T, and let u1 and u2 be equality-preserving expressions such that decltype((u1)) and decltype((u2)) are each U.
T and U model common_­with<T, U> only if:
  • C(t1) equals C(t2) if and only if t1 equals t2, and
  • C(u1) equals C(u2) if and only if u1 equals u2.
[Note 2:
Users can customize the behavior of common_­with by specializing the common_­type class template ([meta.trans.other]).
— end note]

18.4.7 Arithmetic concepts [concepts.arithmetic]

template<class T> concept integral = is_­integral_­v<T>; template<class T> concept signed_­integral = integral<T> && is_­signed_­v<T>; template<class T> concept unsigned_­integral = integral<T> && !signed_­integral<T>; template<class T> concept floating_­point = is_­floating_­point_­v<T>;
[Note 1:
signed_­integral can be modeled even by types that are not signed integer types ([basic.fundamental]); for example, char.
— end note]
[Note 2:
unsigned_­integral can be modeled even by types that are not unsigned integer types ([basic.fundamental]); for example, bool.
— end note]

18.4.8 Concept assignable_­from [concept.assignable]

template<class LHS, class RHS> concept assignable_­from = is_lvalue_reference_v<LHS> && common_reference_with<const remove_reference_t<LHS>&, const remove_reference_t<RHS>&> && requires(LHS lhs, RHS&& rhs) { { lhs = std::forward<RHS>(rhs) } -> same_­as<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.
LHS and RHS model assignable_­from<LHS, RHS> 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 1:
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]

18.4.9 Concept swappable [concept.swappable]

Let t1 and t2 be equality-preserving expressions that denote distinct equal objects of type T, and let u1 and u2 similarly denote distinct equal objects of type U.
[Note 1:
t1 and u1 can denote distinct objects, or the same object.
— end note]
An operation exchanges the values denoted by t1 and u1 if and only if the operation modifies neither t2 nor u2 and:
  • If T and U are the same type, the result of the operation is that t1 equals u2 and u1 equals t2.
  • If T and U are different types and common_­reference_­with<decltype((t1)), decltype((u1))> is modeled, the result of the operation is that C(t1) equals C(u2) and C(u1) equals C(t2) where C is common_­reference_­t<decltype((t1)), decltype((u1))>.
The name ranges​::​swap denotes a customization point object ([customization.point.object]).
The expression ranges​::​swap(E1, E2) for subexpressions E1 and E2 is expression-equivalent to an expression S determined as follows:
  • S is (void)swap(E1, E2)223 if E1 or E2 has class or enumeration type ([basic.compound]) and that expression is valid, with overload resolution performed in a context that includes the declaration template<class T> void swap(T&, T&) = delete; and does not include a declaration of ranges​::​swap.
    If the function selected by overload resolution does not exchange the values denoted by E1 and E2, the program is ill-formed, no diagnostic required.
  • Otherwise, if E1 and E2 are lvalues of array types ([basic.compound]) with equal extent and ranges​::​swap(*E1, *E2) is a valid expression, S is (void)ranges​::​swap_­ranges(E1, E2), except that noexcept(S) is equal to noexcept(​ranges​::​swap(*E1, *E2)).
  • Otherwise, if E1 and E2 are lvalues of the same type T that models move_­constructible<T> and assignable_­from<T&, T>, S is an expression that exchanges the denoted values.
    S is a constant expression if
    • T is a literal type ([basic.types]),
    • both E1 = std​::​move(E2) and E2 = std​::​move(E1) are constant subexpressions ([defns.const.subexpr]), and
    • the full-expressions of the initializers in the declarations T t1(std::move(E1)); T t2(std::move(E2)); are constant subexpressions.
    noexcept(S) is equal to is_­nothrow_­move_­constructible_­v<T> && is_­nothrow_­move_­assignable_­v<T>.
  • Otherwise, ranges​::​swap(E1, E2) is ill-formed.
    [Note 2:
    This case can result in substitution failure when ranges​::​swap(E1, E2) appears in the immediate context of a template instantiation.
    — end note]
[Note 3:
Whenever ranges​::​swap(E1, E2) is a valid expression, it exchanges the values denoted by E1 and E2 and has type void.
— end note]
template<class T> concept swappable = requires(T& a, T& b) { ranges::swap(a, b); };
template<class T, class U> concept swappable_­with = common_reference_with<T, U> && requires(T&& t, U&& u) { ranges::swap(std::forward<T>(t), std::forward<T>(t)); ranges::swap(std::forward<U>(u), std::forward<U>(u)); ranges::swap(std::forward<T>(t), std::forward<U>(u)); ranges::swap(std::forward<U>(u), std::forward<T>(t)); };
[Note 4:
The semantics of the swappable and swappable_­with concepts are fully defined by the ranges​::​swap customization point object.
— end note]
[Example 1:
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> namespace ranges = std::ranges; template<class T, std::swappable_­with<T> U> void value_swap(T&& t, U&& u) { ranges::swap(std::forward<T>(t), std::forward<U>(u)); } template<std::swappable T> void lv_swap(T& t1, T& t2) { ranges::swap(t1, t2); } namespace N { struct A { int m; }; struct Proxy { A* a; Proxy(A& a) : a{&a} {} friend void swap(Proxy x, Proxy y) { ranges::swap(*x.a, *y.a); } }; Proxy proxy(A& a) { return Proxy{ a }; } } 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]
The name swap is used here unqualified.
 

18.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 1:
Unlike the Cpp17Destructible requirements (Table 32), this concept forbids destructors that are potentially throwing, even if a particular invocation of the destructor does not actually throw.
— end note]

18.4.11 Concept constructible_­from [concept.constructible]

The constructible_­from 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_­from = destructible<T> && is_­constructible_­v<T, Args...>;

18.4.12 Concept default_­initializable [concept.default.init]

template<class T> inline constexpr bool is-default-initializable = see below; // exposition only template<class T> concept default_­initializable = constructible_­from<T> && requires { T{}; } && is-default-initializable<T>;
For a type T, is-default-initializable<T> is true if and only if the variable definition T t; is well-formed for some invented variable t; otherwise it is false.
Access checking is performed as if in a context unrelated to T.
Only the validity of the immediate context of the variable initialization is considered.

18.4.13 Concept move_­constructible [concept.moveconstructible]

template<class T> concept move_­constructible = constructible_­from<T, T> && convertible_­to<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.
T models move_­constructible 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.

18.4.14 Concept copy_­constructible [concept.copyconstructible]

template<class T> concept copy_­constructible = move_constructible<T> && constructible_from<T, T&> && convertible_­to<T&, T> && constructible_from<T, const T&> && convertible_­to<const T&, T> && constructible_from<T, const T> && convertible_­to<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.
T models copy_­constructible only if

18.5 Comparison concepts [concepts.compare]

18.5.1 General [concepts.compare.general]

Subclause [concepts.compare] describes concepts that establish relationships and orderings on values of possibly differing object types.

18.5.2 Boolean testability [concept.booleantestable]

The exposition-only boolean-testable concept specifies the requirements on expressions that are convertible to bool and for which the logical operators ([expr.log.and], [expr.log.or], [expr.unary.op]) have the conventional semantics.
template<class T> concept boolean-testable-impl = convertible_­to<T, bool>; // exposition only
Let e be an expression such that decltype((e)) is T.
T models boolean-testable-impl only if:
  • either remove_­cvref_­t<T> is not a class type, or name lookup for the names operator&& and operator|| within the scope of remove_­cvref_­t<T> as if by class member access lookup ([class.member.lookup]) results in an empty declaration set; and
  • name lookup for the names operator&& and operator|| in the associated namespaces and entities of T ([basic.lookup.argdep]) finds no disqualifying declaration (defined below).
A disqualifying parameter is a function parameter whose declared type P
  • is not dependent on a template parameter, and there exists an implicit conversion sequence ([over.best.ics]) from e to P; or
  • is dependent on one or more template parameters, and either
    • P contains no template parameter that participates in template argument deduction ([temp.deduct.type]), or
    • template argument deduction using the rules for deducing template arguments in a function call ([temp.deduct.call]) and e as the argument succeeds.
A key parameter of a function template D is a function parameter of type cv X or reference thereto, where X names a specialization of a class template that is a member of the same namespace as D, and X contains at least one template parameter that participates in template argument deduction.
[Example 1:
In namespace Z { template<class> struct C {}; template<class T> void operator&&(C<T> x, T y); template<class T> void operator||(C<type_identity_t<T>> x, T y); } the declaration of Z​::​operator&& contains one key parameter, C<T> x, and the declaration of Z​::​operator|| contains no key parameters.
— end example]
A disqualifying declaration is
  • a (non-template) function declaration that contains at least one disqualifying parameter; or
  • a function template declaration that contains at least one disqualifying parameter, where
    • at least one disqualifying parameter is a key parameter; or
    • the declaration contains no key parameters; or
    • the declaration declares a function template that is not visible in its namespace ([namespace.memdef]).
[Note 1:
The intention is to ensure that given two types T1 and T2 that each model boolean-testable-impl, the && and || operators within the expressions declval<T1>() && declval<T2>() and declval<T1>() || declval<T2>() resolve to the corresponding built-in operators.
— end note]
template<class T> concept boolean-testable = // exposition only boolean-testable-impl<T> && requires (T&& t) { { !std::forward<T>(t) } -> boolean-testable-impl; };
Let e be an expression such that decltype((e)) is T.
T models boolean-testable only if bool(e) == !bool(!e).
[Example 2:
The types bool, true_­type ([meta.type.synop]), int*, and bitset<N>​::​reference ([template.bitset]) model boolean-testable.
— end example]

18.5.3 Concept equality_­comparable [concept.equalitycomparable]

template<class T, class U> concept weakly-equality-comparable-with = // exposition only requires(const remove_reference_t<T>& t, const remove_reference_t<U>& u) { { t == u } -> boolean-testable; { t != u } -> boolean-testable; { u == t } -> boolean-testable; { u != t } -> boolean-testable; };
Given types T and U, let t and u be lvalues of types const remove_­reference_­t<T> and const remove_­reference_­t<U> respectively.
T and U model weakly-equality-comparable-with<T, U> only if
  • t == u, u == t, t != u, and u != t have the same domain.
  • bool(u == t) == bool(t == u).
  • bool(t != u) == !bool(t == u).
  • bool(u != t) == bool(t != u).
Let a and b be objects of type T.
T models equality_­comparable only if bool(a == b) is true when a is equal to b ([concepts.equality]), and false otherwise.
[Note 1:
The requirement that the expression a == b is equality-preserving implies that == is transitive and symmetric.
— end note]
template<class T, class U> concept equality_­comparable_­with = equality_comparable<T> && equality_comparable<U> && common_reference_with<const remove_reference_t<T>&, const remove_reference_t<U>&> && equality_comparable< common_reference_t< const remove_reference_t<T>&, const remove_reference_t<U>&>> && weakly-equality-comparable-with<T, U>;
Given types T and U, let t be an lvalue of type const remove_­reference_­t<T>, u be an lvalue of type const remove_­reference_­t<U>, and C be: common_reference_t<const remove_reference_t<T>&, const remove_reference_t<U>&> T and U model equality_­comparable_­with<T, U> only if bool(t == u) == bool(C(t) == C(u)).

18.5.4 Concept totally_­ordered [concept.totallyordered]

template<class T> concept totally_­ordered = equality_­comparable<T> && partially-ordered-with<T, T>;
Given a type T, let a, b, and c be lvalues of type const remove_­reference_­t<T>.
T models totally_­ordered only if
  • Exactly one of bool(a < b), bool(a > b), or bool(a == b) is true.
  • If bool(a < b) and bool(b < c), then bool(a < c).
  • bool(a <= b) == !bool(b < a).
  • bool(a >= b) == !bool(a < b).
template<class T, class U> concept totally_­ordered_­with = totally_­ordered<T> && totally_­ordered<U> && equality_­comparable_­with<T, U> && totally_­ordered< common_reference_t< const remove_reference_t<T>&, const remove_reference_t<U>&>> && partially-ordered-with<T, U>;
Given types T and U, let t be an lvalue of type const remove_­reference_­t<T>, u be an lvalue of type const remove_­reference_­t<U>, and C be: common_reference_t<const remove_reference_t<T>&, const remove_reference_t<U>&> T and U model totally_­ordered_­with<T, U> only if
  • bool(t < u) == bool(C(t) < C(u)).
  • bool(t > u) == bool(C(t) > C(u)).
  • bool(t <= u) == bool(C(t) <= C(u)).
  • bool(t >= u) == bool(C(t) >= C(u)).
  • bool(u < t) == bool(C(u) < C(t)).
  • bool(u > t) == bool(C(u) > C(t)).
  • bool(u <= t) == bool(C(u) <= C(t)).
  • bool(u >= t) == bool(C(u) >= C(t)).

18.6 Object concepts [concepts.object]

This subclause describes concepts that specify the basis of the value-oriented programming style on which the library is based.
template<class T> concept movable = is_­object_­v<T> && move_­constructible<T> && assignable_­from<T&, T> && swappable<T>; template<class T> concept copyable = copy_­constructible<T> && movable<T> && assignable_­from<T&, T&> && assignable_­from<T&, const T&> && assignable_­from<T&, const T>; template<class T> concept semiregular = copyable<T> && default_­initializable<T>; template<class T> concept regular = semiregular<T> && equality_­comparable<T>;
[Note 1:
The semiregular concept is modeled by types that behave similarly to built-in types like int, except that they might not be comparable with ==.
— end note]
[Note 2:
The regular concept is modeled by types that behave similarly to built-in types like int and that are comparable with ==.
— end note]

18.7 Callable concepts [concepts.callable]

18.7.1 General [concepts.callable.general]

The concepts in subclause [concepts.callable] describe the requirements on function objects ([function.objects]) and their arguments.

18.7.2 Concept invocable [concept.invocable]

The invocable concept specifies a relationship between a callable type ([func.def]) F and a set of argument types Args... which can be evaluated by the library function invoke ([func.invoke]).
template<class F, class... Args> concept invocable = requires(F&& f, Args&&... args) { invoke(std::forward<F>(f), std::forward<Args>(args)...); // not required to be equality-preserving };
[Example 1:
A function that generates random numbers can model invocable, since the invoke function call expression is not required to be equality-preserving ([concepts.equality]).
— end example]

18.7.3 Concept regular_­invocable [concept.regularinvocable]

template<class F, class... Args> concept regular_­invocable = invocable<F, Args...>;
The invoke function call expression shall be equality-preserving ([concepts.equality]) and shall not modify the function object or the arguments.
[Note 1:
This requirement supersedes the annotation in the definition of invocable.
— end note]
[Example 1:
A random number generator does not model regular_­invocable.
— end example]
[Note 2:
The distinction between invocable and regular_­invocable is purely semantic.
— end note]

18.7.4 Concept predicate [concept.predicate]

template<class F, class... Args> concept predicate = regular_­invocable<F, Args...> && boolean-testable<invoke_result_t<F, Args...>>;

18.7.5 Concept relation [concept.relation]

template<class R, class T, class U> concept relation = predicate<R, T, T> && predicate<R, U, U> && predicate<R, T, U> && predicate<R, U, T>;

18.7.6 Concept equivalence_­relation [concept.equiv]

template<class R, class T, class U> concept equivalence_­relation = relation<R, T, U>;
A relation models equivalence_­relation only if it imposes an equivalence relation on its arguments.

18.7.7 Concept strict_­weak_­order [concept.strictweakorder]

template<class R, class T, class U> concept strict_­weak_­order = relation<R, T, U>;
A relation models strict_­weak_­order only if it imposes a strict weak ordering on its arguments.
The term strict refers to the requirement of an irreflexive relation (!comp(x, x) for all x), and the term weak to requirements that are not as strong as those for a total ordering, but stronger than those for a partial ordering.
If we define equiv(a, b) as !comp(a, b) && !comp(b, a), then the requirements are that comp and equiv both be transitive relations:
  • comp(a, b) && comp(b, c) implies comp(a, c)
  • equiv(a, b) && equiv(b, c) implies equiv(a, c)
[Note 1:
Under these conditions, it can be shown that
  • equiv is an equivalence relation,
  • comp induces a well-defined relation on the equivalence classes determined by equiv, and
  • the induced relation is a strict total ordering.
— end note]