17 Templates [temp]

17.4 Template constraints [temp.constr]

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
]