369 lines
20 KiB
Markdown
369 lines
20 KiB
Markdown
[temp.constr.constr]
|
||
|
||
# 13 Templates [[temp]](./#temp)
|
||
|
||
## 13.5 Template constraints [[temp.constr]](temp.constr#constr)
|
||
|
||
### 13.5.2 Constraints [temp.constr.constr]
|
||
|
||
#### [13.5.2.1](#general) General [[temp.constr.constr.general]](temp.constr.constr.general)
|
||
|
||
[1](#general-1)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/templates.tex#L1610)
|
||
|
||
A [*constraint*](#def:constraint "13.5.2.1 General [temp.constr.constr.general]") is a sequence of logical operations and
|
||
operands that specifies requirements on template arguments[.](#general-1.sentence-1)
|
||
|
||
The operands of a logical operation are constraints[.](#general-1.sentence-2)
|
||
|
||
There are five different kinds of constraints:
|
||
|
||
- [(1.1)](#general-1.1)
|
||
|
||
conjunctions ([[temp.constr.op]](#temp.constr.op "13.5.2.2 Logical operations")),
|
||
|
||
- [(1.2)](#general-1.2)
|
||
|
||
disjunctions ([[temp.constr.op]](#temp.constr.op "13.5.2.2 Logical operations")),
|
||
|
||
- [(1.3)](#general-1.3)
|
||
|
||
atomic constraints ([[temp.constr.atomic]](#temp.constr.atomic "13.5.2.3 Atomic constraints")),
|
||
|
||
- [(1.4)](#general-1.4)
|
||
|
||
concept-dependent constraints ([[temp.constr.concept]](#temp.constr.concept "13.5.2.4 Concept-dependent constraints")), and
|
||
|
||
- [(1.5)](#general-1.5)
|
||
|
||
fold expanded constraints ([[temp.constr.fold]](#temp.constr.fold "13.5.2.5 Fold expanded constraint"))[.](#general-1.sentence-3)
|
||
|
||
[2](#general-2)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/templates.tex#L1623)
|
||
|
||
In order for a constrained template to be instantiated ([[temp.spec]](temp.spec "13.9 Template instantiation and specialization")),
|
||
its [associated constraints](temp.constr.decl#def:associated_constraints "13.5.3 Constrained declarations [temp.constr.decl]") shall be satisfied as described in the following subclauses[.](#general-2.sentence-1)
|
||
|
||
[*Note [1](#general-note-1)*:
|
||
|
||
Forming the name of a specialization of
|
||
a class template,
|
||
a variable template, or
|
||
an alias template ([[temp.names]](temp.names "13.3 Names of template specializations"))
|
||
requires the satisfaction of its constraints[.](#general-2.sentence-2)
|
||
|
||
[Overload resolution](over.match.viable "12.2.3 Viable functions [over.match.viable]") requires the satisfaction of constraints
|
||
on functions and function templates[.](#general-2.sentence-3)
|
||
|
||
â *end note*]
|
||
|
||
#### [13.5.2.2](#temp.constr.op) Logical operations [[temp.constr.op]](temp.constr.op)
|
||
|
||
[1](#temp.constr.op-1)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/templates.tex#L1640)
|
||
|
||
There are two binary logical operations on constraints: conjunction
|
||
and disjunction[.](#temp.constr.op-1.sentence-1)
|
||
|
||
[*Note [1](#temp.constr.op-note-1)*:
|
||
|
||
These logical operations have no corresponding C++ syntax[.](#temp.constr.op-1.sentence-2)
|
||
|
||
For the purpose of exposition, conjunction is spelled
|
||
using the symbol â§ and disjunction is spelled using the
|
||
symbol ⨠[.](#temp.constr.op-1.sentence-3)
|
||
|
||
The operands of these operations are called the left
|
||
and right operands[.](#temp.constr.op-1.sentence-4)
|
||
|
||
In the constraint A â§ B,A is the left operand, and B is the right operand[.](#temp.constr.op-1.sentence-5)
|
||
|
||
â *end note*]
|
||
|
||
[2](#temp.constr.op-2)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/templates.tex#L1653)
|
||
|
||
A [*conjunction*](#def:conjunction "13.5.2.2 Logical operations [temp.constr.op]") is a constraint taking two
|
||
operands[.](#temp.constr.op-2.sentence-1)
|
||
|
||
To determine if a conjunction is[*satisfied*](#def:constraint,satisfaction,conjunction "13.5.2.2 Logical operations [temp.constr.op]"),
|
||
the satisfaction of
|
||
the first operand is checked[.](#temp.constr.op-2.sentence-2)
|
||
|
||
If that is not satisfied, the conjunction is not satisfied[.](#temp.constr.op-2.sentence-3)
|
||
|
||
Otherwise, the conjunction is satisfied if and only if the second
|
||
operand is satisfied[.](#temp.constr.op-2.sentence-4)
|
||
|
||
[3](#temp.constr.op-3)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/templates.tex#L1664)
|
||
|
||
A [*disjunction*](#def:disjunction "13.5.2.2 Logical operations [temp.constr.op]") is a constraint taking two
|
||
operands[.](#temp.constr.op-3.sentence-1)
|
||
|
||
To determine if a disjunction is[*satisfied*](#def:constraint,satisfaction,disjunction "13.5.2.2 Logical operations [temp.constr.op]"),
|
||
the satisfaction of
|
||
the first operand is checked[.](#temp.constr.op-3.sentence-2)
|
||
|
||
If that is satisfied, the disjunction is satisfied[.](#temp.constr.op-3.sentence-3)
|
||
|
||
Otherwise, the disjunction is satisfied if and only if the second
|
||
operand is satisfied[.](#temp.constr.op-3.sentence-4)
|
||
|
||
[4](#temp.constr.op-4)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/templates.tex#L1676)
|
||
|
||
[*Example [1](#temp.constr.op-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)
|
||
|
||
In the satisfaction of the [associated constraints](temp.constr.decl#def:associated_constraints "13.5.3 Constrained declarations [temp.constr.decl]") of f, the constraint sizeof(char) > 1 is not satisfied;
|
||
the second operand is not checked for satisfaction[.](#temp.constr.op-4.sentence-1)
|
||
|
||
â *end example*]
|
||
|
||
[5](#temp.constr.op-5)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/templates.tex#L1695)
|
||
|
||
[*Note [2](#temp.constr.op-note-2)*:
|
||
|
||
A logical negation expression ([[expr.unary.op]](expr.unary.op "7.6.2.2 Unary operators")) is an atomic constraint;
|
||
the negation operator is not treated as a logical operation on constraints[.](#temp.constr.op-5.sentence-1)
|
||
|
||
As a result, distinct negation [*constraint-expression*](temp.constr.decl#nt:constraint-expression "13.5.3 Constrained declarations [temp.constr.decl]")*s* that are equivalent under [[temp.over.link]](temp.over.link "13.7.7.2 Function template overloading") do not subsume one another under [[temp.constr.order]](temp.constr.order "13.5.5 Partial ordering by constraints")[.](#temp.constr.op-5.sentence-2)
|
||
|
||
Furthermore, if substitution to determine
|
||
whether an atomic constraint is satisfied ([[temp.constr.atomic]](#temp.constr.atomic "13.5.2.3 Atomic constraints"))
|
||
encounters a substitution failure, the constraint is not satisfied,
|
||
regardless of the presence of a negation operator[.](#temp.constr.op-5.sentence-3)
|
||
|
||
[*Example [2](#temp.constr.op-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]](#temp.constr.atomic "13.5.2.3 Atomic constraints"))// are not formed from the same [*expression*](expr.comma#nt:expression "7.6.20 Comma operator [expr.comma]")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_sadtemplate <class T> int f3(T) requires (!sad<typename T::type>);int i3 = f3(42); // error: associated constraints not satisfied due to substitution failuretemplate <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
|
||
|
||
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[.](#temp.constr.op-5.sentence-4)
|
||
|
||
â *end example*]
|
||
|
||
â *end note*]
|
||
|
||
#### [13.5.2.3](#temp.constr.atomic) Atomic constraints [[temp.constr.atomic]](temp.constr.atomic)
|
||
|
||
[1](#temp.constr.atomic-1)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/templates.tex#L1738)
|
||
|
||
An [*atomic constraint*](#def:constraint,atomic "13.5.2.3 Atomic constraints [temp.constr.atomic]") 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*](#def:parameter_mapping "13.5.2.3 Atomic constraints [temp.constr.atomic]") ([[temp.constr.decl]](temp.constr.decl "13.5.3 Constrained declarations"))[.](#temp.constr.atomic-1.sentence-1)
|
||
|
||
[*Note [1](#temp.constr.atomic-note-1)*:
|
||
|
||
Atomic constraints are formed by [constraint normalization](temp.constr.normal#def:constraint,normalization "13.5.4 Constraint normalization [temp.constr.normal]")[.](#temp.constr.atomic-1.sentence-2)
|
||
|
||
E is never a [logical and expression](expr.log.and "7.6.14 Logical AND operator [expr.log.and]") nor a [logical or expression](expr.log.or "7.6.15 Logical OR operator [expr.log.or]")[.](#temp.constr.atomic-1.sentence-3)
|
||
|
||
â *end note*]
|
||
|
||
[2](#temp.constr.atomic-2)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/templates.tex#L1753)
|
||
|
||
Two atomic constraints, e1 and e2, are[*identical*](#def:atomic_constraint,identical "13.5.2.3 Atomic constraints [temp.constr.atomic]") if they are formed from the same appearance of the same[*expression*](expr.comma#nt:expression "7.6.20 Comma operator [expr.comma]") and if, given a hypothetical template A whose [*template-parameter-list*](temp.pre#nt:template-parameter-list "13.1 Preamble [temp.pre]") consists of[*template-parameter*](temp.param#nt:template-parameter "13.2 Template parameters [temp.param]")*s* corresponding and equivalent ([[temp.over.link]](temp.over.link "13.7.7.2 Function template overloading")) to
|
||
those mapped by the parameter mappings of the expression,
|
||
a [*template-id*](temp.names#nt:template-id "13.3 Names of template specializations [temp.names]") naming A whose [*template-argument*](temp.names#nt:template-argument "13.3 Names of template specializations [temp.names]")*s* are
|
||
the targets of the parameter mapping of e1 is the same ([[temp.type]](temp.type "13.6 Type equivalence")) as
|
||
a [*template-id*](temp.names#nt:template-id "13.3 Names of template specializations [temp.names]") naming A whose [*template-argument*](temp.names#nt:template-argument "13.3 Names of template specializations [temp.names]")*s* are
|
||
the targets of the parameter mapping of e2[.](#temp.constr.atomic-2.sentence-1)
|
||
|
||
[*Note [2](#temp.constr.atomic-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]](temp.alias "13.7.8 Alias templates"))[.](#temp.constr.atomic-2.sentence-2)
|
||
|
||
[*Example [1](#temp.constr.atomic-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 Nâ¦2 * M + 1template <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*]
|
||
|
||
As specified in [[temp.over.link]](temp.over.link "13.7.7.2 Function template overloading"),
|
||
if the validity or meaning of the program depends on
|
||
whether two constructs are equivalent, and
|
||
they are functionally equivalent but not equivalent,
|
||
the program is ill-formed, no diagnostic required[.](#temp.constr.atomic-2.sentence-3)
|
||
|
||
[*Example [2](#temp.constr.atomic-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*]
|
||
|
||
â *end note*]
|
||
|
||
[3](#temp.constr.atomic-3)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/templates.tex#L1819)
|
||
|
||
To determine if an atomic constraint is[*satisfied*](#def:constraint,satisfaction,atomic "13.5.2.3 Atomic constraints [temp.constr.atomic]"),
|
||
the parameter mapping and template arguments are
|
||
first substituted into its expression[.](#temp.constr.atomic-3.sentence-1)
|
||
|
||
If substitution results in an invalid type or expression
|
||
in the immediate context of the atomic constraint ([[temp.deduct.general]](temp.deduct.general "13.10.3.1 General")),
|
||
the constraint is not satisfied[.](#temp.constr.atomic-3.sentence-2)
|
||
|
||
Otherwise, the [lvalue-to-rvalue conversion](conv.lval "7.3.2 Lvalue-to-rvalue conversion [conv.lval]") is performed if necessary,
|
||
and E shall be a constant expression of type bool[.](#temp.constr.atomic-3.sentence-3)
|
||
|
||
The constraint is satisfied if and only if evaluation of E results in true[.](#temp.constr.atomic-3.sentence-4)
|
||
|
||
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[.](#temp.constr.atomic-3.sentence-5)
|
||
|
||
[*Example [3](#temp.constr.atomic-example-3)*: template<typename T> concept C =sizeof(T) == 4 && !true; // requires atomic constraints sizeof(T) == 4 and !truetemplate<typename T> struct S {constexpr operator bool() const { return true; }};
|
||
|
||
template<typename T> requires (S<T>{})void f(T); // #1void f(int); // #2void 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*]
|
||
|
||
#### [13.5.2.4](#temp.constr.concept) Concept-dependent constraints [[temp.constr.concept]](temp.constr.concept)
|
||
|
||
[1](#temp.constr.concept-1)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/templates.tex#L1857)
|
||
|
||
A [*concept-dependent constraint*](#def:constraint,concept-dependent "13.5.2.4 Concept-dependent constraints [temp.constr.concept]") CD is
|
||
an atomic constraint whose expression is a concept-id CI whose[*concept-name*](temp.concept#nt:concept-name "13.7.9 Concept definitions [temp.concept]") names a dependent concept named C[.](#temp.constr.concept-1.sentence-1)
|
||
|
||
[2](#temp.constr.concept-2)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/templates.tex#L1862)
|
||
|
||
To determine if CD is[*satisfied*](#def:constraint,satisfaction,concept-dependent "13.5.2.4 Concept-dependent constraints [temp.constr.concept]"),
|
||
the parameter mapping and template arguments are first
|
||
substituted into C[.](#temp.constr.concept-2.sentence-1)
|
||
|
||
If substitution results in an invalid concept-id in
|
||
the immediate context of the constraint ([[temp.deduct.general]](temp.deduct.general "13.10.3.1 General")),
|
||
the constraint is not satisfied[.](#temp.constr.concept-2.sentence-2)
|
||
|
||
Otherwise, let CIâ² be
|
||
the normal form ([[temp.constr.normal]](temp.constr.normal "13.5.4 Constraint normalization")) of the concept-id
|
||
after substitution of C[.](#temp.constr.concept-2.sentence-3)
|
||
|
||
[*Note [1](#temp.constr.concept-note-1)*:
|
||
|
||
Normalization of CI might be ill-formed; no diagnostics is required[.](#temp.constr.concept-2.sentence-4)
|
||
|
||
â *end note*]
|
||
|
||
[3](#temp.constr.concept-3)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/templates.tex#L1877)
|
||
|
||
To form CIâ²â²,
|
||
each appearance of C's template parameters in
|
||
the parameter mappings of the atomic constraints
|
||
(including concept-dependent constraints)
|
||
in CIâ² is substituted with their respective arguments from
|
||
the parameter mapping of CD and the arguments of CI[.](#temp.constr.concept-3.sentence-1)
|
||
|
||
[4](#temp.constr.concept-4)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/templates.tex#L1886)
|
||
|
||
CD is satisfied if CIâ²â² is satisfied[.](#temp.constr.concept-4.sentence-1)
|
||
|
||
[*Note [2](#temp.constr.concept-note-2)*:
|
||
|
||
Checking whether CIâ²â² is satisfied
|
||
can lead to further normalization of concept-dependent constraints[.](#temp.constr.concept-4.sentence-2)
|
||
|
||
â *end note*]
|
||
|
||
[*Example [1](#temp.constr.concept-example-1)*: template<typename>concept C = true;
|
||
|
||
template<typename T, template<typename> concept CC>concept D = CC<T>;
|
||
|
||
template<typename U, template<typename> concept CT, template<typename, template<typename> concept> concept CU>int f() requires CU<U, CT>;int i = f<int, C, D>();
|
||
|
||
In this example, the associated constraints of f consist of a concept-dependent constraint
|
||
whose expression is the concept-id CU<U, CT> with the mappingUâ¦U,CTâ¦CT,CUâ¦CU[.](#temp.constr.concept-4.sentence-3)
|
||
|
||
The result of substituting D into this expression is D<U, CT>[.](#temp.constr.concept-4.sentence-4)
|
||
|
||
We consider the normal form of the resulting concept-id,
|
||
which is CC<T> with the mappingTâ¦U,CCâ¦CT[.](#temp.constr.concept-4.sentence-5)
|
||
|
||
By recursion, C is substituted into CC<T>, and the result
|
||
is normalized to the atomic constraint true, which is satisfied[.](#temp.constr.concept-4.sentence-6)
|
||
|
||
â *end example*]
|
||
|
||
#### [13.5.2.5](#temp.constr.fold) Fold expanded constraint [[temp.constr.fold]](temp.constr.fold)
|
||
|
||
[1](#temp.constr.fold-1)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/templates.tex#L1920)
|
||
|
||
A [*fold expanded constraint*](#def:constraint,fold_expanded "13.5.2.5 Fold expanded constraint [temp.constr.fold]") is formed from a constraint C and
|
||
a [*fold-operator*](expr.prim.fold#nt:fold-operator "7.5.7 Fold expressions [expr.prim.fold]") which can either be && or ||[.](#temp.constr.fold-1.sentence-1)
|
||
|
||
A fold expanded constraint is a pack expansion ([[temp.variadic]](temp.variadic "13.7.4 Variadic templates"))[.](#temp.constr.fold-1.sentence-2)
|
||
|
||
Let N be the number of elements
|
||
in the pack expansion parameters ([[temp.variadic]](temp.variadic "13.7.4 Variadic templates"))[.](#temp.constr.fold-1.sentence-3)
|
||
|
||
[2](#temp.constr.fold-2)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/templates.tex#L1928)
|
||
|
||
A fold expanded constraint whose [*fold-operator*](expr.prim.fold#nt:fold-operator "7.5.7 Fold expressions [expr.prim.fold]") is && is satisfied if it is a valid pack expansion and
|
||
if N=0 or if for each i where 0â¤i<N in increasing order,C is satisfied
|
||
when replacing each pack expansion parameter
|
||
with the corresponding ith element[.](#temp.constr.fold-2.sentence-1)
|
||
|
||
No substitution takes place for any i greater than
|
||
the smallest i for which the constraint is not satisfied[.](#temp.constr.fold-2.sentence-2)
|
||
|
||
[3](#temp.constr.fold-3)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/templates.tex#L1938)
|
||
|
||
A fold expanded constraint whose [*fold-operator*](expr.prim.fold#nt:fold-operator "7.5.7 Fold expressions [expr.prim.fold]") is || is satisfied if it is a valid pack expansion,N>0, and if for i where 0â¤i<N in increasing order,
|
||
there is a smallest i for which C is satisfied
|
||
when replacing each pack expansion parameter
|
||
with the corresponding ith element[.](#temp.constr.fold-3.sentence-1)
|
||
|
||
No substitution takes place for any i greater than
|
||
the smallest i for which the constraint is satisfied[.](#temp.constr.fold-3.sentence-2)
|
||
|
||
[4](#temp.constr.fold-4)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/templates.tex#L1948)
|
||
|
||
[*Note [1](#temp.constr.fold-note-1)*:
|
||
|
||
If the pack expansion expands packs of different size,
|
||
then it is invalid and the fold expanded constraint is not satisfied[.](#temp.constr.fold-4.sentence-1)
|
||
|
||
â *end note*]
|
||
|
||
[5](#temp.constr.fold-5)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/templates.tex#L1954)
|
||
|
||
Two fold expanded constraints are [*compatible for subsumption*](#def:subsumption,compatible_for "13.5.2.5 Fold expanded constraint [temp.constr.fold]") if their respective constraints both contain
|
||
an equivalent unexpanded pack ([[temp.over.link]](temp.over.link "13.7.7.2 Function template overloading"))[.](#temp.constr.fold-5.sentence-1)
|