[Note 1: *end note*]

Subclause [temp.constr] 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]).

— 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 subclauses.

[Note 1: *end 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.

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

[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); // has associated constraint sizeof(T) > 1 ∧ get_value<T>()
void f(int);
f('a'); // OK: calls f(int)
*end example*]

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.

— [Note 2: — *end 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-expressions
that 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 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); // ambiguous, !sad<T> atomic constraint expressions ([temp.constr.atomic])
// are not formed from the same expression
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); // OK, !sad<T> atomic constraint expressions both come from not_sad
template <class T> int f3(T) requires (!sad<typename T::type>);
int i3 = f3(42); // error: associated constraints not satisfied due to substitution failure
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); // OK, substitution failure contained within sad_nested_type
*end example*]

Here,
requires (!sad<typename T::type>) requires
that there is a nested type that is not sad,
whereas
requires (!sad_nested_type<T>) requires
that there is no sad nested type.

— An atomic constraint is formed from
an expression E
and 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]).

Two atomic constraints, and , are
identical
if they are formed from the same appearance of the same
expression
and if, given a hypothetical template A
whose template-parameter-list consists of
template-parameters corresponding and equivalent ([temp.over.link]) to
those mapped by the parameter mappings of the expression,
a template-id naming A
whose template-arguments are
the targets of the parameter mapping of
is the same ([temp.type]) as
a template-id naming A
whose template-arguments are
the targets of the parameter mapping of .

[Note 2: — *end 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 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>(); // OK, the atomic constraints from concept C in both fs are Atomic<N>
// with mapping similar to
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); // OK, there is only one g
}
— *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>(); // ill-formed, no diagnostic required:
// requires determination of subsumption between atomic constraints that are
// functionally equivalent but not equivalent
}
— *end example*]

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.

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; // 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*]

A template declaration ([temp.pre])
or templated function declaration ([dcl.fct])
can be constrained by the use of a requires-clause.

This allows the specification of constraints for that declaration as
an expression:

Constraints can also be associated with a declaration through the use of
type-constraints
in a template-parameter-list or parameter-type-list.

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

A declaration's associated constraints are defined as follows:

- Otherwise, if there is a single introduced constraint-expression, the associated constraints are the normal form of that expression.
- Otherwise, the associated constraints are the normal form of a logical AND expression whose operands are in the following order:
- the constraint-expression introduced by each type-constraint ([temp.param]) in the declaration's template-parameter-list, in order of appearance, and
- the constraint-expression introduced by a requires-clause following a template-parameter-list ([temp.pre]), and
- the constraint-expression introduced by each type-constraint in the parameter-type-list of a function declaration, and
- the constraint-expression introduced by a trailing requires-clause ([dcl.decl]) of a function declaration ([dcl.fct]).

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

[Example 1: 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>;

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);

template<C1 T> requires C2<T> void f6();
template<C2 T> requires C1<T> void f7();
— *end example*]

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

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

[Example 2: template <class T> concept C = true;
template <class T> struct A {
template <class U> U f(U) requires C<typename T::type>; // #1
template <class U> U f(U) requires C<T>; // #2
};
template <> template <class U>
U A<int>::f(U u) requires C<int> { return u; } // OK, specializes #2
*end example*]

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.

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

- The normal form of a concept-id C<A, A, ..., A> 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 1: 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 ) ∨ 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 V&* 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.

[Note 1: *end 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]).

— [Example 2: 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

—*end example*]

—

A constraint P subsumes a constraint Q
if and only if,
for every disjunctive clause
in the disjunctive normal form131
of P, subsumes every conjunctive clause
in the conjunctive normal form132
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 A and B are identical using the rules described in [temp.constr.atomic].

[Note 1: *end note*]

The subsumption relation defines a partial ordering on constraints.

This partial ordering is used to determine

— - the best viable candidate of non-template functions ([over.match.best]),
- the address of a non-template function ([over.over]),
- the matching of template template arguments,
- the partial ordering of class template specializations, and
- the partial ordering of function templates.

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 2: 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*]