[temp.constr.constr] (original) (raw)

13 Templates [temp]

13.5 Template constraints [temp.constr]

13.5.2 Constraints [temp.constr.constr]

13.5.2.1 General [temp.constr.constr.general]

A constraint is a sequence of logical operations and operands that specifies requirements on template arguments.

The operands of a logical operation are constraints.

There are three different kinds of constraints:

In order for a constrained template to be instantiated ([temp.spec]), its associated constraintsshall be satisfied as described in the following subclauses.

[Note 1:

Forming the name of a specialization of a class template, a variable template, or an alias template ([temp.names]) requires the satisfaction of its constraints.

Overload resolutionrequires the satisfaction of constraints on functions and function templates.

— _end note_]

13.5.2.2 Logical operations [temp.constr.op]

There are two binary logical operations on constraints: conjunction and disjunction.

[Note 1:

These logical operations have no corresponding C++ syntax.

For the purpose of exposition, conjunction is spelled using the symbol ∧ and disjunction is spelled using the symbol ∨.

The operands of these operations are called the left and right operands.

In the constraint ,A is the left operand, and B is the right operand.

— _end note_]

A conjunction is a constraint taking two operands.

To determine if a conjunction issatisfied, the satisfaction of the first operand is checked.

If that is not satisfied, the conjunction is not satisfied.

Otherwise, the conjunction is satisfied if and only if the second operand is satisfied.

A disjunction is a constraint taking two operands.

To determine if a disjunction issatisfied, the satisfaction of the first operand is checked.

If that is satisfied, the disjunction is satisfied.

Otherwise, the disjunction is satisfied if and only if the second operand is satisfied.

[Example 1: template<typename T> constexpr bool get_value() { return T::value; } template<typename T> requires (sizeof(T) > 1) && (get_value<T>()) void f(T); void f(int); f('a');

In the satisfaction of the associated constraintsof f, the constraint sizeof(char) > 1 is not satisfied; the second operand is not checked for satisfaction.

— _end example_]

[Note 2:

A logical negation expression ([expr.unary.op]) is an atomic constraint; the negation operator is not treated as a logical operation on constraints.

Furthermore, if substitution to determine whether an atomic constraint is satisfied ([temp.constr.atomic]) encounters a substitution failure, the constraint is not satisfied, regardless of the presence of a negation operator.

[Example 2: template <class T> concept sad = false;template <class T> int f1(T) requires (!sad<T>);template <class T> int f1(T) requires (!sad<T>) && true;int i1 = f1(42); template <class T> concept not_sad = !sad<T>;template <class T> int f2(T) requires not_sad<T>;template <class T> int f2(T) requires not_sad<T> && true;int i2 = f2(42); template <class T> int f3(T) requires (!sad<typename T::type>);int i3 = f3(42); template <class T> concept sad_nested_type = sad<typename T::type>;template <class T> int f4(T) requires (!sad_nested_type<T>);int i4 = f4(42);

Here,requires (!sad<typename T​::​type>) requires that there is a nested type that is not sad, whereasrequires (!sad_­nested_­type<T>) requires that there is no sad nested type.

— _end example_]

— _end note_]

13.5.2.3 Atomic constraints [temp.constr.atomic]

An atomic constraint is formed from an expression Eand a mapping from the template parameters that appear within E to template arguments that are formed via substitution during constraint normalization in the declaration of a constrained entity (and, therefore, can involve the unsubstituted template parameters of the constrained entity), called the parameter mapping ([temp.constr.decl]).

[Note 2:

The comparison of parameter mappings of atomic constraints operates in a manner similar to that of declaration matching with alias template substitution ([temp.alias]).

[Example 1: template <unsigned N> constexpr bool Atomic = true;template <unsigned N> concept C = Atomic<N>;template <unsigned N> concept Add1 = C<N + 1>;template <unsigned N> concept AddOne = C<N + 1>;template <unsigned M> void f() requires Add1<2 * M>;template <unsigned M> int f() requires AddOne<2 * M> && true;int x = f<0>(); template <unsigned N> struct WrapN;template <unsigned N> using Add1Ty = WrapN<N + 1>;template <unsigned N> using AddOneTy = WrapN<N + 1>;template <unsigned M> void g(Add1Ty<2 * M> *);template <unsigned M> void g(AddOneTy<2 * M> *);void h() { g<0>(nullptr); } — _end example_]

This similarity includes the situation where a program is ill-formed, no diagnostic required, when the meaning of the program depends on whether two constructs are equivalent, and they are functionally equivalent but not equivalent.

[Example 2: template <unsigned N> void f2() requires Add1<2 * N>;template <unsigned N> int f2() requires Add1<N * 2> && true;void h2() { f2<0>(); } — _end example_]

— _end note_]

To determine if an atomic constraint issatisfied, the parameter mapping and template arguments are first substituted into its expression.

If substitution results in an invalid type or expression, the constraint is not satisfied.

Otherwise, the lvalue-to-rvalue conversionis performed if necessary, and E shall be a constant expression of type bool.

The constraint is satisfied if and only if evaluation of Eresults in true.

If, at different points in the program, the satisfaction result is different for identical atomic constraints and template arguments, the program is ill-formed, no diagnostic required.

[Example 3: template<typename T> concept C = sizeof(T) == 4 && !true; template<typename T> struct S { constexpr operator bool() const { return true; } };template<typename T> requires (S<T>{}) void f(T); void f(int); void g() { f(0); } — _end example_]