From Jason Turner

[temp.constr.constr]

Diff to HTML by rtfpessoa

Files changed (1) hide show
  1. tmp/tmp_6d17k66/{from.md → to.md} +223 -0
tmp/tmp_6d17k66/{from.md → to.md} RENAMED
@@ -0,0 +1,223 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ### Constraints <a id="temp.constr.constr">[[temp.constr.constr]]</a>
2
+
3
+ A *constraint* is a sequence of logical operations and operands that
4
+ specifies requirements on template arguments. The operands of a logical
5
+ operation are constraints. There are three different kinds of
6
+ constraints:
7
+
8
+ - conjunctions [[temp.constr.op]],
9
+ - disjunctions [[temp.constr.op]], and
10
+ - atomic constraints [[temp.constr.atomic]].
11
+
12
+ In order for a constrained template to be instantiated [[temp.spec]],
13
+ its associated constraints [[temp.constr.decl]] shall be satisfied as
14
+ described in the following subclauses.
15
+
16
+ [*Note 1*: Forming the name of a specialization of a class template, a
17
+ variable template, or an alias template [[temp.names]] requires the
18
+ satisfaction of its constraints. Overload resolution
19
+ [[over.match.viable]] requires the satisfaction of constraints on
20
+ functions and function templates. — *end note*]
21
+
22
+ #### Logical operations <a id="temp.constr.op">[[temp.constr.op]]</a>
23
+
24
+ There are two binary logical operations on constraints: conjunction and
25
+ disjunction.
26
+
27
+ [*Note 1*: These logical operations have no corresponding C++ syntax.
28
+ For the purpose of exposition, conjunction is spelled using the symbol ∧
29
+ and disjunction is spelled using the symbol ∨. The operands of these
30
+ operations are called the left and right operands. In the constraint
31
+ A ∧ B, A is the left operand, and B is the right operand. — *end note*]
32
+
33
+ A *conjunction* is a constraint taking two operands. To determine if a
34
+ conjunction is *satisfied*, the satisfaction of the first operand is
35
+ checked. If that is not satisfied, the conjunction is not satisfied.
36
+ Otherwise, the conjunction is satisfied if and only if the second
37
+ operand is satisfied.
38
+
39
+ A *disjunction* is a constraint taking two operands. To determine if a
40
+ disjunction is *satisfied*, the satisfaction of the first operand is
41
+ checked. If that is satisfied, the disjunction is satisfied. Otherwise,
42
+ the disjunction is satisfied if and only if the second operand is
43
+ satisfied.
44
+
45
+ [*Example 1*:
46
+
47
+ ``` cpp
48
+ template<typename T>
49
+ constexpr bool get_value() { return T::value; }
50
+
51
+ template<typename T>
52
+ requires (sizeof(T) > 1) && (get_value<T>())
53
+ void f(T); // has associated constraint sizeof(T) > 1 ∧ get_value<T>()
54
+
55
+ void f(int);
56
+
57
+ f('a'); // OK: calls f(int)
58
+ ```
59
+
60
+ In the satisfaction of the associated constraints [[temp.constr.decl]]
61
+ of `f`, the constraint `sizeof(char) > 1` is not satisfied; the second
62
+ operand is not checked for satisfaction.
63
+
64
+ — *end example*]
65
+
66
+ [*Note 2*:
67
+
68
+ A logical negation expression [[expr.unary.op]] is an atomic constraint;
69
+ the negation operator is not treated as a logical operation on
70
+ constraints. As a result, distinct negation *constraint-expression*s
71
+ that are equivalent under  [[temp.over.link]] do not subsume one another
72
+ under  [[temp.constr.order]]. Furthermore, if substitution to determine
73
+ whether an atomic constraint is satisfied [[temp.constr.atomic]]
74
+ encounters a substitution failure, the constraint is not satisfied,
75
+ regardless of the presence of a negation operator.
76
+
77
+ [*Example 2*:
78
+
79
+ ``` cpp
80
+ template <class T> concept sad = false;
81
+
82
+ template <class T> int f1(T) requires (!sad<T>);
83
+ template <class T> int f1(T) requires (!sad<T>) && true;
84
+ int i1 = f1(42); // ambiguous, !sad<T> atomic constraint expressions[temp.constr.atomic]
85
+ // are not formed from the same expression
86
+
87
+ template <class T> concept not_sad = !sad<T>;
88
+ template <class T> int f2(T) requires not_sad<T>;
89
+ template <class T> int f2(T) requires not_sad<T> && true;
90
+ int i2 = f2(42); // OK, !sad<T> atomic constraint expressions both come from not_sad
91
+
92
+ template <class T> int f3(T) requires (!sad<typename T::type>);
93
+ int i3 = f3(42); // error: associated constraints not satisfied due to substitution failure
94
+
95
+ template <class T> concept sad_nested_type = sad<typename T::type>;
96
+ template <class T> int f4(T) requires (!sad_nested_type<T>);
97
+ int i4 = f4(42); // OK, substitution failure contained within sad_nested_type
98
+ ```
99
+
100
+ Here, `requires (!sad<typename T::type>)` requires that there is a
101
+ nested `type` that is not `sad`, whereas
102
+ `requires (!sad_nested_type<T>)` requires that there is no `sad` nested
103
+ `type`.
104
+
105
+ — *end example*]
106
+
107
+ — *end note*]
108
+
109
+ #### Atomic constraints <a id="temp.constr.atomic">[[temp.constr.atomic]]</a>
110
+
111
+ An *atomic constraint* is formed from an expression `E` and a mapping
112
+ from the template parameters that appear within `E` to template
113
+ arguments that are formed via substitution during constraint
114
+ normalization in the declaration of a constrained entity (and,
115
+ therefore, can involve the unsubstituted template parameters of the
116
+ constrained entity), called the *parameter mapping*
117
+ [[temp.constr.decl]].
118
+
119
+ [*Note 1*: Atomic constraints are formed by constraint normalization
120
+ [[temp.constr.normal]]. `E` is never a logical expression
121
+ [[expr.log.and]] nor a logical expression
122
+ [[expr.log.or]]. — *end note*]
123
+
124
+ Two atomic constraints, e₁ and e₂, are *identical* if they are formed
125
+ from the same appearance of the same *expression* and if, given a
126
+ hypothetical template A whose *template-parameter-list* consists of
127
+ *template-parameter*s corresponding and equivalent [[temp.over.link]] to
128
+ those mapped by the parameter mappings of the expression, a
129
+ *template-id* naming A whose *template-argument*s are the targets of the
130
+ parameter mapping of e₁ is the same [[temp.type]] as a *template-id*
131
+ naming A whose *template-argument*s are the targets of the parameter
132
+ mapping of e₂.
133
+
134
+ [*Note 2*:
135
+
136
+ The comparison of parameter mappings of atomic constraints operates in a
137
+ manner similar to that of declaration matching with alias template
138
+ substitution [[temp.alias]].
139
+
140
+ [*Example 1*:
141
+
142
+ ``` cpp
143
+ template <unsigned N> constexpr bool Atomic = true;
144
+ template <unsigned N> concept C = Atomic<N>;
145
+ template <unsigned N> concept Add1 = C<N + 1>;
146
+ template <unsigned N> concept AddOne = C<N + 1>;
147
+ template <unsigned M> void f()
148
+ requires Add1<2 * M>;
149
+ template <unsigned M> int f()
150
+ requires AddOne<2 * M> && true;
151
+
152
+ int x = f<0>(); // OK, the atomic constraints from concept C in both fs are Atomic<N>
153
+ // with mapping similar to `N` ↦ `2 * M + 1`
154
+
155
+ template <unsigned N> struct WrapN;
156
+ template <unsigned N> using Add1Ty = WrapN<N + 1>;
157
+ template <unsigned N> using AddOneTy = WrapN<N + 1>;
158
+ template <unsigned M> void g(Add1Ty<2 * M> *);
159
+ template <unsigned M> void g(AddOneTy<2 * M> *);
160
+
161
+ void h() {
162
+ g<0>(nullptr); // OK, there is only one g
163
+ }
164
+ ```
165
+
166
+ — *end example*]
167
+
168
+ This similarity includes the situation where a program is ill-formed, no
169
+ diagnostic required, when the meaning of the program depends on whether
170
+ two constructs are equivalent, and they are functionally equivalent but
171
+ not equivalent.
172
+
173
+ [*Example 2*:
174
+
175
+ ``` cpp
176
+ template <unsigned N> void f2()
177
+ requires Add1<2 * N>;
178
+ template <unsigned N> int f2()
179
+ requires Add1<N * 2> && true;
180
+ void h2() {
181
+ f2<0>(); // ill-formed, no diagnostic required:
182
+ // requires determination of subsumption between atomic constraints that are
183
+ // functionally equivalent but not equivalent
184
+ }
185
+ ```
186
+
187
+ — *end example*]
188
+
189
+ — *end note*]
190
+
191
+ To determine if an atomic constraint is *satisfied*, the parameter
192
+ mapping and template arguments are first substituted into its
193
+ expression. If substitution results in an invalid type or expression,
194
+ the constraint is not satisfied. Otherwise, the lvalue-to-rvalue
195
+ conversion [[conv.lval]] is performed if necessary, and `E` shall be a
196
+ constant expression of type `bool`. The constraint is satisfied if and
197
+ only if evaluation of `E` results in `true`. If, at different points in
198
+ the program, the satisfaction result is different for identical atomic
199
+ constraints and template arguments, the program is ill-formed, no
200
+ diagnostic required.
201
+
202
+ [*Example 3*:
203
+
204
+ ``` cpp
205
+ template<typename T> concept C =
206
+ sizeof(T) == 4 && !true; // requires atomic constraints sizeof(T) == 4 and !true
207
+
208
+ template<typename T> struct S {
209
+ constexpr operator bool() const { return true; }
210
+ };
211
+
212
+ template<typename T> requires (S<T>{})
213
+ void f(T); // #1
214
+ void f(int); // #2
215
+
216
+ void g() {
217
+ f(0); // error: expression S<int>{} does not have type bool
218
+ } // while checking satisfaction of deduced arguments of #1;
219
+ // call is ill-formed even though #2 is a better match
220
+ ```
221
+
222
+ — *end example*]
223
+