[expr.prim.req] (original) (raw)
7 Expressions [expr]
7.5 Primary expressions [expr.prim]
7.5.8 Requires expressions [expr.prim.req]
7.5.8.1 General [expr.prim.req.general]
A requires-expression provides a concise way to express requirements on template arguments that can be checked by name lookupor by checking properties of types and expressions.
A requires-expression is a prvalue of type boolwhose value is described below.
[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 arequires-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 therequires-clause, and the second introduces the requires-expression.
— _end example_]
A local parameter of a requires-expression shall not have a default argument.
The type of such a parameter is determined as specified for a function parameter in [dcl.fct].
These parameters have no linkage, storage, or lifetime; they are only used as notation for the purpose of defining requirements.
[Example 2: template<typename T> concept C = requires(T t, ...) { t;};template<typename T> concept C2 = requires(T p[2]) { (decltype(p))nullptr; }; — _end example_]
The substitution of template arguments into a requires-expressioncan result in the formation of invalid types or expressions in the immediate context of its requirements ([temp.deduct.general]) 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 requirementwould always result in a substitution failure, the program is ill-formed; no diagnostic required.
[Example 3: template<typename T> concept C = requires { new decltype((void)T{}); }; — _end example_]
7.5.8.2 Simple requirements [expr.prim.req.simple]
[Note 1:
The enclosing requires-expression will evaluate to falseif substitution of template arguments into the expression fails.
— _end note_]
[Example 1: template<typename T> concept C = requires (T a, T b) { a + b; }; — _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-requirementand a nested-requirement.
— _end note_]
7.5.8.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 falseif 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; typename S<T>; typename Ref<T>; }; — _end example_]
7.5.8.4 Compound requirements [expr.prim.req.compound]
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.
[Example 1:
Given concepts C and D,requires { { E1 } -> C;{ E2 } -> D<A, ⋯, A>;};is equivalent torequires { E1; requires C<decltype((E1))>; E2; requires D<decltype((E2)), A, ⋯, A>;};(including in the case where n is zero).
— _end example_]
- Substitution of template arguments (if any) into the return-type-requirement is performed.
[Example 2: template<typename T> concept C1 = requires(T x) { {x++};};
The compound-requirement in C1requires 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 C2requires 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 C3requires that g(x) is a valid expression and that g(x) is non-throwing.
— _end example_]
7.5.8.5 Nested requirements [expr.prim.req.nested]
A nested-requirement can be used to specify additional constraints in terms of local parameters.
[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_]