17 Templates [temp]

17.4 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
]

17.4.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 constraints shall be satisfied as described in the following subsections.
[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 resolution requires the satisfaction of constraints on functions and function templates.
end note
]

17.4.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 is satisfied, 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 is satisfied, 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<typename T>
  constexpr bool get_value() { return T::value; }

template<typename T>
  requires (sizeof(T) > 1) && get_value<T>()
    void f(T);      // has associated constraint sizeof(T) > 1  get_­value<T>()

void f(int);

f('a'); // OK: calls f(int)
In the satisfaction of the associated constraints of f, the constraint sizeof(char) > 1 is not satisfied; the second operand is not checked for satisfaction.
end example
]

17.4.1.2 Atomic constraints [temp.constr.atomic]

An atomic constraint is formed from an expression E and a mapping from the template parameters that appear within E to template arguments involving the template parameters of the constrained entity, called the parameter mapping ([temp.constr.decl]).
[Note
:
Atomic constraints are formed by constraint normalization.
end note
]
Two atomic constraints are identical if they are formed from the same expression and the targets of the parameter mappings are equivalent according to the rules for expressions described in [temp.over.link].
To determine if an atomic constraint is satisfied, 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 conversion is performed if necessary, and E shall be a constant expression of type bool.
The constraint is satisfied if and only if evaluation of E results in true.
[Example
:
template<typename T> concept C =
  sizeof(T) == 4 && !true;      // requires atomic constraints sizeof(T) == 4 and !true

template<typename T> struct S {
  constexpr operator bool() const { return true; }
};

template<typename T> requires (S<T>{})
void f(T);                      // #1
void f(int);                    // #2

void g() {
  f(0);                         // error: expression S<int>{} does not have type bool
}                               // while checking satisfaction of deduced arguments of #1;
                                // call is ill-formed even though #2 is a better match
end example
]

17.4.2 Constrained declarations [temp.constr.decl]

A template declaration or function declaration can be constrained by the use of a requires-clause.
This allows the specification of constraints for that declaration as an expression:
constraint-expression:
	logical-or-expression
Constraints can also be associated with a declaration through the use of constrained-parameters in a template-parameter-list.
Each of these forms introduces additional constraint-expressions that are used to constrain the declaration.
A template'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<typename T> concept C = true;

template<C T> void f1(T);
template<typename T> requires C<T> void f2(T);
template<typename T> void f3(T) requires C<T>;
The functions f1, f2, and f3 have the associated constraint C<T>.
template<typename T> concept C1 = true;
template<typename T> concept C2 = sizeof(T) > 0;

template<C1 T> void f4(T) requires C2<T>;
template<typename T> requires C1<T> && C2<T> void f5(T);
The associated constraints of f4 and f5 are C1<T> C2<T>.
template<C1 T> requires C2<T> void f6();
template<C2 T> requires C1<T> void f7();
The associated constraints of f6 are C1<T> C2<T>, and those of f7 are C2<T> C1<T>.
end example
]

17.4.3 Constraint normalization [temp.constr.normal]

The normal form of an expression E is a constraint that is defined as follows:
  • The normal form of an expression ( E ) is the normal form of E.
  • The normal form of an expression E1 || E2 is the disjunction of the normal forms of E1 and E2.
  • The normal form of an expression E1 && E2 is the conjunction of the normal forms of E1 and E2.
  • The normal form of an id-expression of the form C<A, A, ..., A>, where C names a concept, is the normal form of the constraint-expression of C, after substituting A, A, ..., A for C's respective template parameters in the parameter mappings in each atomic constraint.
    If any such substitution results in an invalid type or expression, the program is ill-formed; no diagnostic is required.
    [Example
    :
    template<typename T> concept A = T::value || true;
    template<typename U> concept B = A<U*>;
    template<typename V> concept C = B<V&>;
    Normalization of B's constraint-expression is valid and results in T​::​value (with the mapping T) true (with an empty mapping), despite the expression T​::​value being ill-formed for a pointer type T.
    Normalization of C's constraint-expression results in the program being ill-formed, because it would form the invalid type T&* in the parameter mapping.
    end example
    ]
  • The normal form of any other expression E is the atomic constraint whose expression is E and whose parameter mapping is the identity mapping.
The process of obtaining the normal form of a constraint-expression is called normalization.
[Note
:
Normalization of constraint-expressions is performed when determining the associated constraints ([temp.constr.constr]) of a declaration and when evaluating the value of an id-expression that names a concept specialization ([expr.prim.id]).
end note
]
[Example
:
template<typename T> concept C1 = sizeof(T) == 1;
template<typename T> concept C2 = C1<T>() && 1 == 2;
template<typename T> concept C3 = requires { typename T::type; };
template<typename T> concept C4 = requires (T x) { ++x; }

template<C2 U> void f1(U);      // #1
template<C3 U> void f2(U);      // #2
template<C4 U> void f3(U);      // #3
The associated constraints of #1 are sizeof(T) == 1 (with mapping TU) 1 == 2.

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

The associated constraints of #3 are requires (T x) { ++x; } (with mapping TU).
end example
]

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

A constraint P subsumes a constraint Q if and only if, for every disjunctive clause in the disjunctive normal form138 of P, subsumes every conjunctive clause in the conjuctive normal form139 of Q, where
  • a disjunctive clause subsumes a conjunctive clause if and only if there exists an atomic constraint in for which there exists an atomic constraint in such that subsumes , and
  • an atomic constraint A subsumes another atomic constraint B if and only if the A and B are identical using the rules described in [temp.constr.atomic].
[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.
This partial ordering is used to determine
end note
]
A declaration D1 is at least as constrained as a declaration D2 if
  • D1 and D2 are both constrained declarations and D1's associated constraints subsume those of D2; or
  • D2 has no associated constraints.
A declaration D1 is more constrained than another declaration D2 when D1 is at least as constrained as D2, and D2 is not at least as constrained as D1.
[Example
:
template<typename T> concept C1 = requires(T t) { --t; };
template<typename T> concept C2 = C1<T> && requires(T t) { *t; };

template<C1 T> void f(T);       // #1
template<C2 T> void f(T);       // #2
template<typename T> void g(T); // #3
template<C1 T> void g(T);       // #4

f(0);                           // selects #1
f((int*)0);                     // selects #2
g(true);                        // selects #3 because C1<bool> is not satisfied
g(0);                           // selects #4
end example
]
A constraint is in disjunctive normal form when it is a disjunction of clauses where each clause is a conjunction of atomic constraints.
[Example
:
For atomic constraints A, B, and C, the disjunctive normal form of the constraint is .
Its disjunctive clauses are and .
end example
]
A constraint is in conjunctive normal form when it is a conjunction of clauses where each clause is a disjunction of atomic constraints.
[Example
:
For atomic constraints A, B, and C, the constraint is in conjunctive normal form.
Its conjunctive clauses are A and .
end example
]