-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfoo-conceptname.html
executable file
·236 lines (218 loc) · 16.1 KB
/
foo-conceptname.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
<!-- vim: set ts=2 sw=2 tw=80 fo=tc et -->
<!DOCTYPE html>
<html>
<head>
<title>Revisiting the meaning of foo(ConceptName,ConceptName)</title>
<style>
code { white-space: pre; }
th { text-align: left; }
</style>
</head>
<body>
<h2>Revisiting the meaning of <code>foo(ConceptName,ConceptName)</code></h2>
<table>
<tr>
<th>Document number:</th><td>P0464R2</td>
</tr>
<tr>
<th rowspan=2>Authors:</th><td>Tony van Eerd <[email protected]></td>
</tr>
<tr>
<td>Botond Ballo <[email protected]></td>
</tr>
<tr>
<th>Date:</th><td>2017-03-12</td>
</tr>
<tr>
<th>Audience:</th><td>Evolution Working Group</td>
</tr>
</table>
<h3>Abstract</h3>
<p>In the current <a href="http://wg21.link/n4553">Concepts Technical Specification</a><sup>[1]</sup>, <code> R foo(ConceptName, ConceptName); </code> denotes a function that takes two arguments of the same type, with that type satisfying the concept <code>ConceptName</code>. More precisely, it's a shorthand for:
<code>
template <ConceptName __C>
R foo(__C a, __C b);
</code></p>
<p>This paper argues that it would be more natural for it to denote a function that takes two arguments of potentially different types, with those types satisfying the concept <code>ConceptName</code>. That is, it should be a shorthand for:
<code>
template <ConceptName __C1, ConceptName __C2>
R foo(__C1 a, __C2 b);
</code>
</p>
<h3>Revision History</h3>
<ul>
<li><strong>R2</strong>
<ul>
<li>New section under "Arguments": "Consistency with dynamic polymorphism"</li>
<li>Expanded "User confusion" section with more recent feedback from users</li>
<li>Added mention of consistency with <code>auto</code> to "Impact of the return type" section</li>
</ul>
</li>
<li><strong>R1</strong>
<ul>
<li>New sections under "Arguments": "Variadic templates" and "Perfect forwarding"</li>
<li>New section under "Counter-arguments": "Interaction with definition checking"</li>
<li>New section "Impact on the return type"</li>
</ul>
</li>
<li><strong>R0</strong>
<ul>
<li>Initial draft</li>
</ul>
</li>
</ul>
<h3>Arguments</h3>
<h4>Follows from first principles</h4>
<p>Once a developer has learned the basic language rule in Concepts that <code> ConceptName var </code> means that the variable <code>var</code> must have some type that models the concept <code>ConceptName</code>, the meaning of <code> ConceptName a, ConceptName b </code> should be obvious: that <code>a</code> and <code>b</code> model <code>ConceptName</code> — no more, no less.</p>
<p>It is surprising for the actual meaning to be "<code>a</code> and <code>b</code> model <code>ConceptName</code> <strong>and</strong> they have the same type". If this additional constraint is desired, it should be stated in the code.</p>
<p>In other words, the interpretation this paper argues for is the one that follows from first principles. The authors believe this should be the guiding consideration.</p>
<h4>Consistency with <code>auto</code></h4>
<p><code>auto</code>, as standardized in C++14, already behaves the way we desire. That is,
<code>
R foo(auto a, auto b);
</code></p>
<p>is a shorthand for:
<code>
template <typename __C1, typename __C2>
R foo(__C1 a, __C2 b);
</code></p>
<p>Since <code>auto</code> can be thought of as the weakest concept, it would make the language simpler and easier to teach if the way concepts behave is consistent with the way <code>auto</code> behaves.</p>
<h4>Consistency with local variables</h4>
<p>The current "same type" rule does not extend to local variables declared in the body of the function:
<code>
// a and b must have the same type, but var is allowed to be a different type
R foo(ConceptName a, ConceptName b) {
ConceptName var = /* ... */;
}
</code></p>
<p>It is very confusing to for two of the three uses of <code>ConceptName</code> in this piece of code to have an additional constraint between them, but not the third.</p>
<h4>Consistency with dynamic polymorphism</h4>
<p>Templates and concepts can be thought of as the static counterpart to the dynamic polymorphism achieved via inheritance.</p>
<p>In a dynamic polymorphism scenario, if <code>Writable</code> is a base class, and you have a function with the following signature:
<code>
void foo(Writable& a, Writable& b);
</code></p>
<p>there is no requirement that the concrete (derived) types of the arguments are the same.</p>
<p>It's surprising, then, if we want to use static polymorphism and make <code>Writable</code> a concept, that the concrete types of the arguments are now required to be the same.</p>
<h4>Variadic templates</h4>
<p>The current behaviour is also inconsistent with the behaviour of variadic templates:
<code>
R foo(ConceptName... args); // args can have different types
</code></p>
<p>This means that the property that a variadic template behaves as if you had written a bunch of overloads with different numbers of parameters, does not hold when the parameter types are specified by a concept name.</p>
<p>It also means that the common pattern of writing your variadic template like so:
<code>
R foo(ConceptName arg); // handle one argument (base case)
R foo(ConceptName arg1, ConceptName arg2, ConceptName... rest); // handle two or more aguments
</code></p>
<p>results in a big surprise: <code>arg1</code> and <code>arg2</code> must have the same type, while the remaining arguments may have different types!</p>
<h4>Concepts are to types as types are to values</h4>
<p>Concepts are to types as types are to values, in the sense that a concept defines a set of valid types much like a type defines a set of valid values. </p>
<p>When we write <code> int x, int y </code>, <code>int</code> is a <em>type</em>, and the meaning is that <code>x</code> and <code>y</code> both have <em>type</em> <code>int</code>; beyond that, <code>x</code> and <code>y</code> are not required to have the same <em>value</em>.</p>
<p>Similar, when we write <code> ConceptName x, ConceptName y </code>, <code>ConceptName</code> is a <em>concept</em>, and the meaning is that <code>x</code> and <code>y</code> both model the <em>concept</em> <code>ConceptName</code>; beyond that, <code>x</code> and <code>y</code> should not be required to have the same <em>type</em>.</p>
<h4>Iterator pairs are going out of fashion</h4>
<p>One of the main arguments given for the current semantics is that having two arguments of the same (templated) type is very common in the standard library, due to iterator pairs.</p>
<p>However, in a modern C++ world, this argument does not hold water. Thanks to the <a href="http://wg21.link/n4569">Ranges Technical Specification</a><sup>[2]</sup>, iterator pairs are going out of fashion, being replaced with iterator/sentinel pairs — which are two potentially <em>different</em> types — and single range objects.
<h4>User confusion</h4>
<p>Users have repeatedly expressed confusion about the current behaviour, indicating that it is not intuitive. Some examples:
<ul>
<li><a href="https://groups.google.com/a/isocpp.org/d/topic/concepts/BFmvN_w-PEs/discussion">Concepts mailing list discussion</a><sup>[3]</sup></li>
<li><a href="https://groups.google.com/a/isocpp.org/forum/#!topic/std-proposals/_YpRok7LaEU/discussion">std-proposals discussion</a><sup>[4]</sup></li>
<li><a href="https://www.reddit.com/r/cpp/comments/53i9a2/conceptlite_short_notation_problem/">Reddit question</a><sup>[5]</sup></li>
</ul></p>
<p>In addition, during the February 2017 meeting in Kona, Hal Finkel sent an email to authors of GitHub projects that use Concepts, asking their feedback
on a number of proposed changes to the Concepts TS, including this one.
<ul>
<li>Of the three users that responded with feedback on this proposal <strong>all three</strong>
(<a href="http://lists.isocpp.org/ext/2017/03/2349.php">1</a>, <a href="http://lists.isocpp.org/ext/2017/03/2345.php">2</a>,
<a href="http://lists.isocpp.org/ext/2017/03/2342.php">3</a>) expressed support for the proposed change.</li>
<li>One of them <a href="http://lists.isocpp.org/ext/2017/03/2345.php">reported</a> that they had been surprised when they previously realized
that the semantics weren't <em>already</em> what we are proposing.</li>
<li>Another <a href="http://lists.isocpp.org/ext/2017/03/2342.php">reported</a> that they just realized <em>now</em>, upon reading (an earlier
version of) this paper, that the semantics aren't already what we are proposing, and were quite surprised.
</ul>
This, in the authors' opinion, is strong evidence that the intuitive semantics is the one we are proposing.</p>
<h4>Perfect forwarding</h4>
<p>The "same type" requirement can be overly restrictive in situations involving perfect forwarding. Consider:
<code>
void foo(Container&& a, Container&& b);
// ...
std::vector<int> a;
foo(a, std::vector<int>{1, 2, 3}); // error: cannot deduce 'Container'
</code></p>
<p>Here, even though the arguments have the same type, they have different value categories, and as a result, due to the use of perfect forwarding, the specific parameter types differ by a reference, preventing deduction.</p>
<h3>How would we express the old meaning?</h3>
<p>What if one wants to express the old meaning of <code> foo(ConceptName,ConceptName) </code>, a function with two
parameters of the same type that satisfies a concept? In addition to the non-terse notation:
<code>
template <ConceptName C>
R foo(C a, C b);
</code></p>
<p>one could also write:
<code>
R foo(ConceptName a, decltype(a) b);
</code>
</p>
<p>A third alternative would be provided by the syntax proposed in <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3878.pdf">N3878</a><sup>[6]</sup>:
<code>
R foo(ConceptName{C} a, C b);
</code></p>
<p>Granted, the second two forms are not exactly equivalent, because the second parameter does not participate in deduction. One can envision a variation of the syntax proposed in N3878 which would mean "same type, and treated equally for deduction":
<code>
R foo(ConceptName{C} a, ConceptName{C} b);
</code></p>
<p>In any case, using notation available today, if equal treatment for deduction matters, the non-terse notation can be used.</p>
<h3>Counter-arguments</h3>
<h4>Frequency of use</h4>
<p>It can be argued that you don't often want a function that takes two arguments of potentially different types satisfying the same concept without having an additional relationship between the two types.</p>
<p>The authors believe that arguments like this based on frequency of use, should take a back seat to the arguments listed above, notably the argument based on first principles.</p>
<p>However, even from a frequency point of view, it should be noted that a <a href="https://groups.google.com/a/isocpp.org/forum/#!topic/concepts/cdX4X0ngJqc/discussion">survey of standard library functions</a><sup>[7]</sup> found a significant amount of "same concept, different type" functions, comparable to the amount of "same concept, same type" functions when controlling for iterator pairs.</p>
<h4>Interaction with definition checking</h4>
<p>It can also be argued that this change will encourage template authors to write under-constrained templates, because they will opt to use the terse <code> R foo(ConceptName, ConceptName); </code> form even in cases where in there should be an additional constraint on the parameter types. A proliferation of under-constrained templates will make the introduction of definition checking harder, because an under-constrained template will not pass definition checking.</p>
<p>The authors acknowledge that this is a concern, but believe that the problem of under-constrained templates is not specific to this change. Template authors will sometimes write under-constrained templates without this change, too, such as by writing <code> R foo(Concept1, Concept2) </code> when an additional relationship between the types is present.</p>
<p>Chances are that, due to the presence of under-constrained templates (irrespective of this change), definition checking will be a opt-in feature anyways.</p>
<h3>Impact on the return type</h3>
<p>The case where a concept name appears both in a parameter type and a return type also needs to be addressed.</p>
<p>Currently, the following code:
<code>
ConceptName foo(ConceptName arg);
</code></p>
<p>is a shorthand for:
<code>
template <ConceptName __C>
__C foo(__C arg);
</code></p>
<p>With this proposal, for consistency, it ought to become a shorthand for:
<code>
template <ConceptName __C>
ConceptName foo(__C arg);
</code></p>
<p>That is, the return type changes from being determined by the parameter type, to being deduced from the types of return expressions (while still being constrained by <code>ConceptName</code>). This has the implication that if the return type isn't deducible (e.g. because different return expressions have different types, or one return expression is a braced-init-list (as in <code>return { exprs }</code>)), the function becomes ill-formed.</p>
<p>(Note that this is consistent with how <code> auto foo(auto arg); </code> behaves.)</p>
<p>If the original semantics is desired, the non-terse notation can be used:
<code>
template <ConceptName C>
C foo(C arg);
</code></p>
<p>Alternatively, using the syntax proposed in N3878:
<code>
auto foo(ConceptName{C} arg) -> C;
</code></p>
<h3>Acknowledgements</h3>
<p>The authors would like to thank Andrew Sutton, Guillaume Racicot, Vadim Petrochenkov, and everyone else who participated in discussions on this
subject (on the public mailing lists, in private email correspondence, and elsewhere), for bringing a variety of valuable perspectives and arguments
to the table.</p>
<p>In addition, the authors would like to Hal Finkel for soliciting feedback from users of Concepts about proposals including this one, and users
including Oleg Davydov, Christopher Di Bella, and Joseph Jevnik, for responding with feedback on this proposal.</p>
<h3>References</h3>
<ol>
<li><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4553.pdf">http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4553.pdf</a></li>
<li><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/n4569.pdf">http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/n4569.pdf</a></li>
<li><a href="https://groups.google.com/a/isocpp.org/d/topic/concepts/BFmvN_w-PEs/discussion">https://groups.google.com/a/isocpp.org/d/topic/concepts/BFmvN_w-PEs/discussion</a></li>
<li><a href="https://groups.google.com/a/isocpp.org/forum/#!topic/std-proposals/_YpRok7LaEU/discussion">https://groups.google.com/a/isocpp.org/forum/#!topic/std-proposals/_YpRok7LaEU/discussion</a></li>
<li><a href="https://www.reddit.com/r/cpp/comments/53i9a2/conceptlite_short_notation_problem/">https://www.reddit.com/r/cpp/comments/53i9a2/conceptlite_short_notation_problem/</a></li>
<li><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3878.pdf">http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3878.pdf</a></li>
<li><a href="https://groups.google.com/a/isocpp.org/forum/#!topic/concepts/cdX4X0ngJqc/discussion">https://groups.google.com/a/isocpp.org/forum/#!topic/concepts/cdX4X0ngJqc/discussion</a></li>
</ol>
</body>
</html>