7 Expressions [expr]

7.5 Primary expressions [expr.prim]

7.5.7 Requires expressions [expr.prim.req]

7.5.7.1 General [expr.prim.req.general]

A requires-expression is a prvalue of type bool whose value is described below.
Expressions appearing within a requirement-body are unevaluated operands ([term.unevaluated.operand]).
[Example 1:
A common use of requires-expressions is to define requirements in concepts such as the one below: template<typename T> concept R = requires (T i) { typename T::type; {*i} -> std::convertible_­to<const typename T::type&>; };
A requires-expression can also be used in a requires-clause ([temp.pre]) as a way of writing ad hoc constraints on template arguments such as the one below: template<typename T> requires requires (T x) { x + x; } T add(T a, T b) { return a + b; }
The first requires introduces the requires-clause, and the second introduces the requires-expression.
— end example]
A requires-expression may introduce local parameters using a parameter-declaration-clause ([dcl.fct]).
A local parameter of a requires-expression shall not have a default argument.
These parameters have no linkage, storage, or lifetime; they are only used as notation for the purpose of defining requirements.
The parameter-declaration-clause of a requirement-parameter-list shall not terminate with an ellipsis.
[Example 2: template<typename T> concept C = requires(T t, ...) { // error: terminates with an ellipsis t; }; — end example]
The substitution of template arguments into a requires-expression may result in the formation of invalid types or expressions in its requirements or the violation of the semantic constraints of those requirements.
In such cases, the requires-expression evaluates to false; it does not cause the program to be ill-formed.
The substitution and semantic constraint checking proceeds in lexical order and stops when a condition that determines the result of the requires-expression is encountered.
If substitution (if any) and semantic constraint checking succeed, the requires-expression evaluates to true.
[Note 1:
If a requires-expression contains invalid types or expressions in its requirements, and it does not appear within the declaration of a templated entity, then the program is ill-formed.
— end note]
If the substitution of template arguments into a requirement would always result in a substitution failure, the program is ill-formed; no diagnostic required.
[Example 3: template<typename T> concept C = requires { new int[-(int)sizeof(T)]; // ill-formed, no diagnostic required }; — end example]

7.5.7.2 Simple requirements [expr.prim.req.simple]

A simple-requirement asserts the validity of an expression.
[Note 1:
The enclosing requires-expression will evaluate to false if substitution of template arguments into the expression fails.
The expression is an unevaluated operand ([term.unevaluated.operand]).
— end note]
[Example 1: template<typename T> concept C = requires (T a, T b) { a + b; // C<T> is true if a + b is a valid expression }; — end example]
A requirement that starts with a requires token is never interpreted as a simple-requirement.
[Note 2:
This simplifies distinguishing between a simple-requirement and a nested-requirement.
— end note]

7.5.7.3 Type requirements [expr.prim.req.type]

A type-requirement asserts the validity of a type.
[Note 1:
The enclosing requires-expression will evaluate to false if substitution of template arguments fails.
— end note]
[Example 1: template<typename T, typename T::type = 0> struct S; template<typename T> using Ref = T&; template<typename T> concept C = requires { typename T::inner; // required nested member name typename S<T>; // required valid ([temp.names]) template-id; // fails if T​::​type does not exist as a type to which 0 can be implicitly converted typename Ref<T>; // required alias template substitution, fails if T is void }; — end example]
A type-requirement that names a class template specialization does not require that type to be complete ([basic.types]).

7.5.7.4 Compound requirements [expr.prim.req.compound]

A compound-requirement asserts properties of the expression E.
Substitution of template arguments (if any) and verification of semantic properties proceed in the following order:
  • Substitution of template arguments (if any) into the expression is performed.
  • If the noexcept specifier is present, E shall not be a potentially-throwing expression ([except.spec]).
  • If the return-type-requirement is present, then:
    • Substitution of template arguments (if any) into the return-type-requirement is performed.
    • The immediately-declared constraint ([temp.param]) of the type-constraint for decltype((E)) shall be satisfied.
      [Example 1:
      Given concepts C and D, requires { { E1 } -> C; { E2 } -> D<A, , A>; }; is equivalent to requires { E1; requires C<decltype((E1))>; E2; requires D<decltype((E2)), A, , A>; }; (including in the case where n is zero).
      — end example]
[Example 2: template<typename T> concept C1 = requires(T x) { {x++}; };
The compound-requirement in C1 requires that x++ is a valid expression.
It is equivalent to the simple-requirement x++;.
template<typename T> concept C2 = requires(T x) { {*x} -> std::same_­as<typename T::inner>; };
The compound-requirement in C2 requires that *x is a valid expression, that typename T​::​inner is a valid type, and that std​::​same_­as<decltype((*x)), typename T​::​inner> is satisfied.
template<typename T> concept C3 = requires(T x) { {g(x)} noexcept; };
The compound-requirement in C3 requires that g(x) is a valid expression and that g(x) is non-throwing.
— end example]

7.5.7.5 Nested requirements [expr.prim.req.nested]

A nested-requirement can be used to specify additional constraints in terms of local parameters.
The constraint-expression shall be satisfied ([temp.constr.decl]) by the substituted template arguments, if any.
Substitution of template arguments into a nested-requirement does not result in substitution into the constraint-expression other than as specified in [temp.constr.constr].
[Example 1:
template<typename U> concept C = sizeof(U) == 1; template<typename T> concept D = requires (T t) { requires C<decltype (+t)>; }; D<T> is satisfied if sizeof(decltype (+t)) == 1 ([temp.constr.atomic]).
— end example]
A local parameter shall only appear as an unevaluated operand ([term.unevaluated.operand]) within the constraint-expression.
[Example 2: template<typename T> concept C = requires (T a) { requires sizeof(a) == 4; // OK requires a == 0; // error: evaluation of a constraint variable }; — end example]