Files
2025-10-25 03:02:53 +03:00

18 KiB
Raw Permalink Blame History

[expr.prim.req]

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]

1

#

A requires-expression provides a concise way to express requirements on template arguments that can be checked by name lookup or by checking properties of types and expressions.

requires-expression:
requires requirement-parameter-listopt requirement-body

requirement-parameter-list:
( parameter-declaration-clause )

requirement-body:
{ requirement-seq }

requirement-seq:
requirement requirement-seqopt

requirement:
simple-requirement
type-requirement
compound-requirement
nested-requirement

2

#

A requires-expression is a prvalue of type bool whose value is described below.

3

#

[Example 1:

A common use of requires-expressions is to define requirements in concepts such as the one below:templateconcept 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:templaterequires 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]

4

#

A requires-expression may introduce local parameters using aparameter-declaration-clause.

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.

The parameter-declaration-clause of arequirement-parameter-list shall not terminate with an ellipsis.

[Example 2: templateconcept C = requires(T t, ...) { // error: terminates with an ellipsis t;};templateconcept C2 = requires(T p[2]) {(decltype(p))nullptr; // OK, p has type “pointer to T''}; — end example]

5

#

The substitution of template arguments into a requires-expression can 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 requirement would always result in a substitution failure, the program is ill-formed; no diagnostic required.

[Example 3: template concept C =requires {new decltype((void)T{}); // ill-formed, no diagnostic required}; — end example]

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

simple-requirement:
expression ;

1

#

A simple-requirement asserts the validity of an expression.

The expression is an unevaluated operand.

[Note 1:

The enclosing requires-expression will evaluate to false if substitution of template arguments into the expression fails.

— end note]

[Example 1: template concept C =requires (T a, T b) { a + b; // C is true if a + b is a valid expression}; — end example]

2

#

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.8.3 Type requirements [expr.prim.req.type]

type-requirement:
typename nested-name-specifieropt type-name ;
typename splice-specifier
typename splice-specialization-specifier

1

#

A type-requirement asserts the validity of a type.

The component names of a type-requirement are those of itsnested-name-specifier (if any) andtype-name (if any).

[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 using Ref = T&;

template concept C = requires {typename T::inner; // required nested member nametypename S; // required valid ([temp.names]) template-id; fails if T::type does not exist as a type// to which 0 can be implicitly convertedtypename Ref; // required alias template substitution, fails if T is voidtypename [:T::r1:]; // fails if T::r1 is not a reflection of a typetypename [:T::r2:]; // fails if T::r2 is not a reflection of a template Z for which Z is a type}; — end example]

2

#

A type-requirement that names a class template specialization does not require that type to be complete ([basic.types.general]).

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

compound-requirement:
{ expression } noexceptopt return-type-requirementopt ;

return-type-requirement:
-> type-constraint

1

#

A compound-requirement asserts properties of the expression E.

The expression is an unevaluated operand.

Substitution of template arguments (if any) and verification of semantic properties proceed in the following order:

  • (1.1)

    Substitution of template arguments (if any) into the expression is performed.

  • (1.2)

    If the noexcept specifier is present,E shall not be a potentially-throwing expression ([except.spec]).

  • (1.3)

    If the return-type-requirement is present, then:

    [Example 1: Given concepts C and D,requires {{ E1 } -> C; { E2 } -> D<A1, ⋯, An>;}; is equivalent torequires { E1; requires C<decltype((E1))>; E2; requires D<decltype((E2)), A1, ⋯, An>;}; (including in the case where n is zero). — end example]

2

#

[Example 2: template concept C1 = requires(T x) {{x++};};

The compound-requirement in C1 requires that x++ is a valid expression.

It is equivalent to the simple-requirementx++;.

template concept C2 = requires(T x) {{*x} -> std::same_as;};

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 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.8.5 Nested requirements [expr.prim.req.nested]

nested-requirement:
requires constraint-expression ;

1

#

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 concept C = sizeof(U) == 1;

template concept D = requires (T t) {requires C<decltype (+t)>;};D is satisfied if sizeof(decltype (+t)) == 1 ([temp.constr.atomic]).

— end example]