[temp.constr] (original) (raw)

13 Templates [temp]

13.5 Template constraints [temp.constr]

[ Note

:

This subclause defines the meaning of constraints on template arguments.

The abstract syntax and satisfaction rules are defined in [temp.constr.constr].

Constraints are associated with declarations in [temp.constr.decl].

Declarations are partially ordered by their associated constraints ([temp.constr.order]).

end note

]

13.5.1 Constraints [temp.constr.constr]

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

:

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.1.1 Logical operations [temp.constr.op]

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

[ Note

:

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

:

template constexpr bool get_value() { return T::value; }

template requires (sizeof(T) > 1) && (get_value()) 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

:

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

As a result, distinct negation constraint-expressionsthat are equivalent under [temp.over.link]do not subsume one another under [temp.constr.order].

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

:

template concept sad = false;

template int f1(T) requires (!sad); template int f1(T) requires (!sad) && true; int i1 = f1(42);

template concept not_sad = !sad; template int f2(T) requires not_sad; template int f2(T) requires not_sad && true; int i2 = f2(42);

template int f3(T) requires (!sad); int i3 = f3(42);

template concept sad_nested_type = sad; template int f4(T) requires (!sad_nested_type); 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.1.2 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

:

Atomic constraints are formed by constraint normalization.

end note

]

[ Note

:

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

:

template constexpr bool Atomic = true; template concept C = Atomic; template concept Add1 = C<N + 1>; template concept AddOne = C<N + 1>; template void f() requires Add1<2 * M>; template int f() requires AddOne<2 * M> && true;

int x = f<0>();

template struct WrapN; template using Add1Ty = WrapN<N + 1>; template using AddOneTy = WrapN<N + 1>; template void g(Add1Ty<2 * M> *); template 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

:

template void f2() requires Add1<2 * N>; template 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

:

template concept C = sizeof(T) == 4 && !true;

template struct S { constexpr operator bool() const { return true; } };

template requires (S{}) void f(T);
void f(int);

void g() { f(0);
}

end example

]

13.5.2 Constrained declarations [temp.constr.decl]

Each of these forms introduces additional constraint-expressionsthat are used to constrain the declaration.

A declaration's associated constraints are defined as follows:

The formation of the associated constraints establishes the order in which constraints are instantiated when checking for satisfaction ([temp.constr.constr]).

[ Example

:

template concept C = true;

template void f1(T); template requires C void f2(T); template void f3(T) requires C;

The functions f1, f2, and f3 have the associated constraint C<T>.

template concept C1 = true; template concept C2 = sizeof(T) > 0;

template void f4(T) requires C2; template requires C1 && C2 void f5(T);

The associated constraints of f4 and f5are C1<T> ∧ C2<T>.

template requires C2 void f6(); template requires C1 void f7();

The associated constraints off6 are C1<T> ∧ C2<T>, and those off7 are C2<T> ∧ C1<T>.

end example

]

When determining whether a given introducedconstraint-expression of a declaration in an instantiated specialization of a templated class is equivalent ([temp.over.link]) to the correspondingconstraint-expression of a declaration outside the class body, is instantiated.

If the instantiation results in an invalid expression, the constraint-expressions are not equivalent.

[ Note

:

This can happen when determining which member template is specialized by an explicit specialization declaration.

end note

]

[ Example

:

template concept C = true; template struct A { template U f(U) requires C;
template U f(U) requires C;
};

template <> template U A::f(U u) requires C { return u; }

Substituting int for T in C<typename T​::​type>produces an invalid expression, so the specialization does not match #1.

Substituting int for T in C<T> produces C<int>, which is equivalent to the constraint-expression for the specialization, so it does match #2.

end example

]

13.5.3 Constraint normalization [temp.constr.normal]

The normal form of an expression E is a constraint that is defined as follows:

The process of obtaining the normal form of aconstraint-expressionis callednormalization.

[ Note

:

Normalization of constraint-expressionsis performed when determining the associated constraints ([temp.constr.constr]) of a declaration and when evaluating the value of an id-expressionthat names a concept specialization ([expr.prim.id]).

end note

]

[ Example

:

template concept C1 = sizeof(T) == 1; template concept C2 = C1 && 1 == 2; template concept C3 = requires { typename T::type; }; template concept C4 = requires (T x) { ++x; }

template void f1(U);
template void f2(U);
template void f3(U);

The associated constraints of #1 aresizeof(T) == 1 (with mapping ) ∧ 1 == 2.

The associated constraints of #2 arerequires { typename T​::​type; } (with mapping ).

The associated constraints of #3 arerequires (T x) { ++x; } (with mapping ).

end example

]

13.5.4 Partial ordering by constraints [temp.constr.order]

A constraint P subsumes a constraint Qif and only if, for every disjunctive clause in the disjunctive normal form130of P, subsumes every conjunctive clause in the conjunctive normal form131of Q, where

[ Example

:

Let A and B be atomic constraints.

The constraint subsumes A, but A does not subsume .

The constraint A subsumes , but does not subsume A.

Also note that every constraint subsumes itself.

end example

]

[ Note

:

The subsumption relation defines a partial ordering on constraints.

end note

]

A declaration D1 isat least as constrained as a declaration D2 if

A declaration D1 is more constrainedthan another declaration D2 when D1 is at least as constrained as D2, and D2 is not at least as constrained as D1.

[ Example

:

template concept C1 = requires(T t) { --t; }; template concept C2 = C1 && requires(T t) { *t; };

template void f(T);
template void f(T);
template void g(T); template void g(T);

f(0);
f((int*)0);
g(true);
g(0);

end example

]