-
Notifications
You must be signed in to change notification settings - Fork 0
/
P1021R1.html
367 lines (347 loc) · 45.7 KB
/
P1021R1.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
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
<!DOCTYPE html>
<html><head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<style>
/**
* SyntaxHighlighter
* http://alexgorbatchev.com/SyntaxHighlighter
*
* SyntaxHighlighter is donationware. If you are using it, please donate.
* http://alexgorbatchev.com/SyntaxHighlighter/donate.html
*
* @version
* 3.0.83 (July 02 2010)
*
* @copyright
* Copyright (C) 2004-2010 Alex Gorbatchev.
*
* @license
* Dual licensed under the MIT and GPL licenses.
*/
.syntaxhighlighter {
background-color: white !important;
}
.syntaxhighlighter .line.alt1 {
background-color: white !important;
}
.syntaxhighlighter .line.alt2 {
background-color: white !important;
}
.syntaxhighlighter .line.highlighted.alt1, .syntaxhighlighter .line.highlighted.alt2 {
background-color: #e0e0e0 !important;
}
.syntaxhighlighter .line.highlighted.number {
color: black !important;
}
.syntaxhighlighter table caption {
color: black !important;
}
.syntaxhighlighter .gutter {
color: #afafaf !important;
}
.syntaxhighlighter .gutter .line {
border-right: 3px solid #6ce26c !important;
}
.syntaxhighlighter .gutter .line.highlighted {
background-color: #6ce26c !important;
color: white !important;
}
.syntaxhighlighter.printing .line .content {
border: none !important;
}
.syntaxhighlighter.collapsed {
overflow: visible !important;
}
.syntaxhighlighter.collapsed .toolbar {
color: blue !important;
background: white !important;
border: 1px solid #6ce26c !important;
}
.syntaxhighlighter.collapsed .toolbar a {
color: blue !important;
}
.syntaxhighlighter.collapsed .toolbar a:hover {
color: red !important;
}
.syntaxhighlighter .toolbar {
color: white !important;
background: #6ce26c !important;
border: none !important;
}
.syntaxhighlighter .toolbar a {
color: white !important;
}
.syntaxhighlighter .toolbar a:hover {
color: black !important;
}
.syntaxhighlighter .plain, .syntaxhighlighter .plain a {
color: black !important;
}
.syntaxhighlighter .comments, .syntaxhighlighter .comments a {
color: #008200 !important;
}
.syntaxhighlighter .string, .syntaxhighlighter .string a {
color: blue !important;
}
.syntaxhighlighter .keyword {
color: #006699 !important;
}
.syntaxhighlighter .preprocessor {
color: gray !important;
}
.syntaxhighlighter .variable {
color: #aa7700 !important;
}
.syntaxhighlighter .value {
color: #009900 !important;
}
.syntaxhighlighter .functions {
color: #ff1493 !important;
}
.syntaxhighlighter .constants {
color: #0066cc !important;
}
.syntaxhighlighter .script {
font-weight: bold !important;
color: #006699 !important;
background-color: none !important;
}
.syntaxhighlighter .color1, .syntaxhighlighter .color1 a {
color: gray !important;
}
.syntaxhighlighter .color2, .syntaxhighlighter .color2 a {
color: #ff1493 !important;
}
.syntaxhighlighter .color3, .syntaxhighlighter .color3 a {
color: red !important;
}
.syntaxhighlighter .keyword {
font-weight: bold !important;
}
.syntaxhighlighter div.toolbar span a.toolbar_item{
display: none !important;
}
body .syntaxhighlighter .line {
white-space: pre !important;
}
.pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.clo,.opn,.pun{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.kwd,.tag,.typ{font-weight:700}.str{color:#060}.kwd{color:#006}.com{color:#600;font-style:italic}.typ{color:#404}.lit{color:#044}.clo,.opn,.pun{color:#440}.tag{color:#006}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee}
</style>
<title>Filling holes in Class Template Argument Deduction</title>
</head>
<body>
<p>P1021R1<br>
Mike Spertus, Symantec<br>
<a href="mailto:mike_spertus@symantec.com">mike_spertus@symantec.com</a><br>
Timur Doumler<br>
<a href="mailto:papers@timur.audio">papers@timur.audio</a><br>
Richard Smith<br>
<a href="mailto:richardsmith@google.com">richardsmith@google.com</a><br>
2018-10-07<br>
Audience: Evolution Working Group
</p>
<h1>Filling holes in Class Template Argument Deduction</h1>
This paper proposes filling several gaps in <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0091r3.html">Class Template Argument Deduction</a>.
<h2>Rationale</h2>
As one of us (Timur) has noted when giving public presentations on using class template argument deduction, there are a significant number of cases where it cannot be used. This always deflates the positive feelings from the rest of the talk because it is accurately regarded as artificially inconsistent. In particular, listeners are invariably surprised that it does not work with aggregate templates, type aliases, inherited constructors, or obey normal rules for calling function templates.
We will show in this paper
that these limitations can be safely removed. Note that some of these items were intentionally deferred from
C++17 with the intent of adding them in C++20. <p>The following example (which also includes
notation from
<a href="http://wg21.link/p1141r1">P1141R1</a>
<a href="#tf" name="f">*</a>) gives an idea of how much filling holes can smooth the road.
</p><table border="1"><tbody><tr><th>C++17 + P1141R1</th><th>Proposed</th></tr>
<tr><td><div><div id="highlighter_608659" class="syntaxhighlighter nogutter cpp"><div class="toolbar"><span><a href="#" class="toolbar_item command_help help">?</a></span></div><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td class="code"><div class="container"><div class="line number1 index0 alt2"><code class="cpp keyword bold">void</code> <code class="cpp plain">f(Curve auto c) {</code></div><div class="line number2 index1 alt1"><code class="cpp spaces"> </code><code class="cpp plain">Point<decltype(c.x_min())> top_left{c.x_min(), c.y_max()};</code></div><div class="line number3 index2 alt2"><code class="cpp spaces"> </code><code class="cpp plain">pmr::vector<</code><code class="cpp keyword bold">typename</code> <code class="cpp plain">decltype(c.ctrl_pts)::value_type> v(c.ctrl_pts.begin(), c.ctr_pts.end());</code></div><div class="line number4 index3 alt1"><code class="cpp spaces"> </code><code class="cpp comments">/* ... */</code></div><div class="line number5 index4 alt2"><code class="cpp plain">}</code></div></div></td></tr></tbody></table></div></div></td>
<td><div><div id="highlighter_36137" class="syntaxhighlighter nogutter cpp"><div class="toolbar"><span><a href="#" class="toolbar_item command_help help">?</a></span></div><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td class="code"><div class="container"><div class="line number1 index0 alt2"><code class="cpp keyword bold">void</code> <code class="cpp plain">f(Curve auto c) {</code></div><div class="line number2 index1 alt1"><code class="cpp spaces"> </code><code class="cpp plain">Point upper_left_bound{c.x_min(), c.y_max()};</code></div><div class="line number3 index2 alt2"><code class="cpp spaces"> </code><code class="cpp plain">pmr::vector v(c.ctrl_pts.begin(), c.ctr_pts.end());</code></div><div class="line number4 index3 alt1"><code class="cpp spaces"> </code><code class="cpp comments">/* ... */</code></div><div class="line number5 index4 alt2"><code class="cpp plain">}</code></div></div></td></tr></tbody></table></div></div></td></tr></tbody></table>
To see that this really falls under the category of filling holes, the above code would work in C++17
if we just gave <tt>Point</tt> a two argument constructor corresponding to aggregate initialization
and used <tt>vector</tt> instead of <tt>pmr::vector</tt>.
<p>
To give a “real-world” example, Mateusz Pusz recently
approached us about his <a href="https://github.com/mpusz/units">Physical Units library</a>,
asking whether code like the following in his library could be made to deduce as expected:
</p><blockquote><div><div id="highlighter_464254" class="syntaxhighlighter nogutter cpp"><div class="toolbar"><span><a href="#" class="toolbar_item command_help help">?</a></span></div><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td class="code"><div class="container"><div class="line number1 index0 alt2"><code class="cpp keyword bold">template</code><code class="cpp plain"><Dimension D, Unit U, </code><code class="cpp keyword bold">typename</code> <code class="cpp plain">Rep> </code><code class="cpp keyword bold">class</code> <code class="cpp plain">quantity;</code></div><div class="line number2 index1 alt1"><code class="cpp keyword bold">template</code><code class="cpp plain"><Dimension D, Unit U, </code><code class="cpp keyword bold">typename</code> <code class="cpp plain">Rep> quantity(Rep r) -> quantity<D, U, Rep>;</code></div><div class="line number3 index2 alt2"> </div><div class="line number4 index3 alt1"><code class="cpp keyword bold">template</code><code class="cpp plain"><Unit U = meter, </code><code class="cpp keyword bold">typename</code> <code class="cpp plain">Rep = intmax_t></code></div><div class="line number5 index4 alt2"><code class="cpp keyword bold">using</code> <code class="cpp plain">length = quantity<dimension_length, U, Rep>;</code></div><div class="line number6 index5 alt1"> </div><div class="line number7 index6 alt2"><code class="cpp plain">length d1(3); </code><code class="cpp comments">// 3 meters</code></div><div class="line number8 index7 alt1"><code class="cpp plain">length d2(3.14); </code><code class="cpp comments">// 3.14 meters</code></div><div class="line number9 index8 alt2"><code class="cpp plain">length<mile> d3(3); </code><code class="cpp comments">// 3 miles</code></div><div class="line number10 index9 alt1"><code class="cpp plain">length<mile> d4(3.14); </code><code class="cpp comments">// 3.14 miles</code></div><div class="line number11 index10 alt2"><code class="cpp plain">length<mile, </code><code class="cpp color1 bold">float</code><code class="cpp plain">> d3(3.14); </code><code class="cpp comments">// 3.14 miles as float</code></div></div></td></tr></tbody></table></div></div></blockquote>
We were pleased to discover on examination that the
proposals in this paper for alias templates, partial
template argument lists, and rules for aligning deduction guides with defaulted template
parameters would have allowed the above code to work just as expected.
<hr>
<a name="tf" href="#f">*</a> For an in-depth discussion on how
terse constraints can benefit from improved inferencing without
requiring named types provided by Class Template
Argument Deduction, see <a href="http://wg21.link/p1168r0">P1168R0</a>.
<hr>
<h2>Class Template Argument Deduction for aggregates</h2>
We propose that Class Template Argument Deduction works for aggregate initialization as one
shouldn't have to choose between having aggregates and deduction. This is well illustrated
by the following example:
<table border="1"><tbody><tr><th>C++17</th><th>Proposed</th></tr>
<tr><td><div><div id="highlighter_153831" class="syntaxhighlighter nogutter cpp"><div class="toolbar"><span><a href="#" class="toolbar_item command_help help">?</a></span></div><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td class="code"><div class="container"><div class="line number1 index0 alt2"><code class="cpp keyword bold">template</code><code class="cpp plain"><</code><code class="cpp keyword bold">class</code> <code class="cpp plain">T></code></div><div class="line number2 index1 alt1"><code class="cpp keyword bold">struct</code> <code class="cpp plain">Point { T x; T y;};</code></div><div class="line number3 index2 alt2"> </div><div class="line number4 index3 alt1"><code class="cpp comments">// Aggregate: Cannot deduce</code></div><div class="line number5 index4 alt2"><code class="cpp plain">Point<</code><code class="cpp color1 bold">double</code><code class="cpp plain">> p{3.0, 4.0};</code></div><div class="line number6 index5 alt1"><code class="cpp plain">Point<</code><code class="cpp color1 bold">double</code><code class="cpp plain">> p2 {.x = 3.0, .y = 4.0};</code></div></div></td></tr></tbody></table></div></div></td>
<td><div><div id="highlighter_642490" class="syntaxhighlighter nogutter cpp"><div class="toolbar"><span><a href="#" class="toolbar_item command_help help">?</a></span></div><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td class="code"><div class="container"><div class="line number1 index0 alt2"><code class="cpp keyword bold">template</code><code class="cpp plain"><</code><code class="cpp keyword bold">class</code> <code class="cpp plain">T></code></div><div class="line number2 index1 alt1"><code class="cpp keyword bold">struct</code> <code class="cpp plain">Point { T x; T y};</code></div><div class="line number3 index2 alt2"> </div><div class="line number4 index3 alt1"><code class="cpp comments">// Proposed: Aggregates deduce</code></div><div class="line number5 index4 alt2"><code class="cpp plain">Point p{3.0, 4.0};</code></div><div class="line number6 index5 alt1"><code class="cpp plain">Point p2 {.x = 3.0, .y = 4.0};</code></div></div></td></tr></tbody></table></div></div></td>
</tr></tbody></table>
In other words, deduction should take place simply from the arguments of a braced or designated initializer during aggregate initialization. This can be accomplished by forming an additional deduction guide for aggregates.
<h3>Algorithm</h3>
<p>In the current Working Draft, an aggregate class is defined as a class with no user-declared or inherited constructors, no private or protected non-static data members, no virtual functions, and no virtual, private or protected base classes. While we would like to generate an aggregate deduction guide for class templates
that comply with these rules, we first need to consider the case where there is a dependent base
class that may have virtual functions, which would violate the rules for aggregates. Fortunately,
that case does not cause a problem because any deduction guides that require one or more arguments
will result in a compile-time error after instantiation, and non-aggregate classes without
user-declared or inherited constructors generate a zero-argument deduction guide anyway.
Based on the above, we can safely generate an aggregate deduction guide for class templates
that comply with aggregate rules.
</p><p>When <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0960r1.html">P0960R0</a> was discussed in Rapperswil, it was voted that in order to allow aggregate initialization from a parenthesized list of arguments, aggregate initialization should proceed as if there was a synthesized constructor. We can use the same approach to also synthesize the required additional deduction guide during class template argument
deduction as follows:
</p><ol>
<li>Given a primary class template <tt>C</tt>, determine whether it satisfies all the conditions for an aggregate class ([dcl.init.aggr]/1.1 - 1.4).</li>
<li>If yes, let <tt>T_1</tt>, ...., <tt>T_n</tt> denote the types of the <tt>N</tt> elements ([dcl.init.aggr]/2) of the aggregate (which may or may not depend on its template arguments).</li>
<li>Form a hypothetical constructor <tt>C(T_1, ..., T_N)</tt>. </li>
<li>For every constructor argument of type <tt>T_i</tt>, if all types <tt>T_i ... T_n</tt> are default-constructible, add a default argument value zero-initializing the argument as if by <tt>T_i = {}</tt>.</li>
<li>For the set of functions and function templates formed for [over.match.class.deduct], add an additional function template derived from this hypothetical constructor as described in [over.match.class.deduct]/1.</li>
</ol>
There is a slight complication resulting from subaggregates, and the fact that nested braces can be omitted when instantiating them:
<blockquote><div><div id="highlighter_734434" class="syntaxhighlighter nogutter cpp"><div class="toolbar"><span><a href="#" class="toolbar_item command_help help">?</a></span></div><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td class="code"><div class="container"><div class="line number1 index0 alt2"><code class="cpp keyword bold">struct</code> <code class="cpp plain">Foo { </code><code class="cpp color1 bold">int</code> <code class="cpp plain">x, y; };</code></div><div class="line number2 index1 alt1"><code class="cpp keyword bold">struct</code> <code class="cpp plain">Bar { Foo f; </code><code class="cpp color1 bold">int</code> <code class="cpp plain">z; };</code></div><div class="line number3 index2 alt2"><code class="cpp plain">Bar bar{1, 2}; </code><code class="cpp comments">// Initializes bar.f.x and bar.f.y to 1; zero-initializes bar.z</code></div></div></td></tr></tbody></table></div></div></blockquote>
In this case, we have two initializers, but they do not map to the two elements of the aggregate type <tt>Bar</tt>, instead initializing the sub-elements of the first subaggregate element of type <tt>Foo</tt>.
<p>For complicated nested aggregates, there are potentially many different combinations of valid mappings of initializers to subaggregate elements. It would be unpractical to create hypothetical constructors for all of those combinations. Additionally, whether or not an aggregate type has subaggregate elements may depend on the template arguments:
</p><blockquote><div><div id="highlighter_611034" class="syntaxhighlighter nogutter cpp"><div class="toolbar"><span><a href="#" class="toolbar_item command_help help">?</a></span></div><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td class="code"><div class="container"><div class="line number1 index0 alt2"><code class="cpp keyword bold">template</code> <code class="cpp plain"><</code><code class="cpp keyword bold">typename</code> <code class="cpp plain">T></code></div><div class="line number2 index1 alt1"><code class="cpp keyword bold">struct</code> <code class="cpp plain">Bar { T f; </code><code class="cpp color1 bold">int</code> <code class="cpp plain">z; }; </code><code class="cpp comments">// T might be a subaggregate!</code></div></div></td></tr></tbody></table></div></div></blockquote>
This information is not available during class template argument deduction, because for this we first need to deduce <tt>T</tt>. We therefore propose simply to avoid all of these problems by prohibiting the omission of nested braces when performing class template argument deduction.
<h2>Class Template Argument Deduction for alias templates</h2>
While Class Template Argument Deduction makes type inferencing easier when constructing classes,
it doesn't work for type aliases, penalizing the use of type aliases and creating unnecessary inconsistency.We propose allowing Class Template Argument Deduction for type aliases as in the following example.
<table border="1"><tbody><tr><th><tt>vector</tt></th><th><tt>pmr::vector</tt> (C++17)</th><th>pmr::vector (proposed)</th></tr>
<tr>
<td>
<div><div id="highlighter_776658" class="syntaxhighlighter nogutter cpp"><div class="toolbar"><span><a href="#" class="toolbar_item command_help help">?</a></span></div><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td class="code"><div class="container"><div class="line number1 index0 alt2"><code class="cpp plain">vector v = {1, 2, 3};</code></div><div class="line number2 index1 alt1"><code class="cpp plain">vector v2(cont.begin(), cont.end());</code></div></div></td></tr></tbody></table></div></div>
</td>
<td><div><div id="highlighter_343467" class="syntaxhighlighter nogutter cpp"><div class="toolbar"><span><a href="#" class="toolbar_item command_help help">?</a></span></div><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td class="code"><div class="container"><div class="line number1 index0 alt2"><code class="cpp plain">pmr::vector<</code><code class="cpp color1 bold">int</code><code class="cpp plain">> v = {1, 2, 3};</code></div><div class="line number2 index1 alt1"><code class="cpp plain">pmr::vector<decltype(cont)::value_type> v2(cont.begin(), cont.end());</code></div></div></td></tr></tbody></table></div></div>
</td>
<td><div><div id="highlighter_223547" class="syntaxhighlighter nogutter cpp"><div class="toolbar"><span><a href="#" class="toolbar_item command_help help">?</a></span></div><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td class="code"><div class="container"><div class="line number1 index0 alt2"><code class="cpp plain">pmr::vector v = {1, 2, 3};</code></div><div class="line number2 index1 alt1"><code class="cpp plain">pmr::vector v2(cont.begin(), cont.end());</code></div></div></td></tr></tbody></table></div></div>
</td>
</tr></tbody></table>
<tt>pmr::vector</tt> also serves to illustrate another interesting case. While one might
be tempted to write
<blockquote><div><div id="highlighter_174382" class="syntaxhighlighter nogutter cpp"><div class="toolbar"><span><a href="#" class="toolbar_item command_help help">?</a></span></div><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td class="code"><div class="container"><div class="line number1 index0 alt2"><code class="cpp plain">pmr::vector pv({1, 2, 3}, mem_res); </code><code class="cpp comments">// Ill-formed (C++17 and proposed)</code></div></div></td></tr></tbody></table></div></div></blockquote>
this example is ill-formed by the normal rules of template argument deduction because
<tt>pmr::memory_resource</tt> fails to
deduce <tt>pmr::polymorphic_allocator<int></tt> in the second argument.
<p>While this is to be expected, one suspects that had class template argument deduction
for alias templates been around when <tt>pmr::vector</tt> was being designed,
the committee would have considered allowing such an inference as safe and useful in this context.
If that was desired, it could easily have been achieved
by indicating that the <code>pmr::allocator</code> template parameter should be considered non-deducible:
</p><blockquote><div><div id="highlighter_550973" class="syntaxhighlighter nogutter cpp"><div class="toolbar"><span><a href="#" class="toolbar_item command_help help">?</a></span></div><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td class="code"><div class="container"><div class="line number1 index0 alt2"><code class="cpp keyword bold">namespace</code> <code class="cpp plain">pmr {</code></div><div class="line number2 index1 alt1"><code class="cpp spaces"> </code><code class="cpp keyword bold">template</code><code class="cpp plain"><</code><code class="cpp keyword bold">typename</code> <code class="cpp plain">T></code></div><div class="line number3 index2 alt2"><code class="cpp spaces"> </code><code class="cpp keyword bold">using</code> <code class="cpp plain">vector = std::vector<T, identity_t<pmr::polymorphic_allocator<T>>>; </code><code class="cpp comments">// See p0887R0 for identity_t</code></div><div class="line number4 index3 alt1"><code class="cpp plain">}</code></div><div class="line number5 index4 alt2"><code class="cpp plain">pmr::vector pv({1, 2, 3}, mem_res); </code><code class="cpp comments">// OK with this definition</code></div></div></td></tr></tbody></table></div></div></blockquote>
Finally, in the spirit of alias templates being simply an alias for the type, we do not propose
allowing the programmer to write explicit deduction guides specifically for an alias
template.
<h3>Algorithm</h3>
For deriving deduction guides for the alias templates from guides in the class, we use the following approach (for which we are very grateful for the invaluable assistance of Richard Smith):
<ol><li>Deduce template parameters for the deduction guide by deducing the right hand side of
the deduction guide from the alias template. We do not require that this deduces all the template
parameters as nondeducible contexts may of course occur in general</li>
<li>Substitute any deductions made back into the deduction guides. Since the previous step may not
have deduced all template parameters of the deduction guide, the new guide may have template
parameters from both the type alias and the original deduction guide.</li>
<li>Derive the corresponding deduction guide for the alias template by
deducing the alias from the result type. Note that a constraint may be necessary
as whether and how to deduce the alias from the result type may depend on the
actual argument types.</li>
<li>The guide generated from the copy deduction guide should be given
the precedence associated with copy deduction guides during overload resolution</li></ol>
Let us illustrate this process with an example. Consider the following example:
<blockquote><div><div id="highlighter_41752" class="syntaxhighlighter nogutter cpp"><div class="toolbar"><span><a href="#" class="toolbar_item command_help help">?</a></span></div><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td class="code"><div class="container"><div class="line number1 index0 alt2"><code class="cpp keyword bold">template</code><code class="cpp plain"><</code><code class="cpp keyword bold">class</code> <code class="cpp plain">T> </code><code class="cpp keyword bold">using</code> <code class="cpp plain">P = pair<</code><code class="cpp color1 bold">int</code><code class="cpp plain">, T>;</code></div></div></td></tr></tbody></table></div></div></blockquote>
Naively using the deduction guides from pair is not ideal because they
cannot necessarily deduce objects of type <tt>P</tt> even
from arguments that should obviously work, like <tt>P({}, 0)</tt>. However,
let us apply the above procedure. The relevant deduction guide is
<blockquote><div><div id="highlighter_368505" class="syntaxhighlighter nogutter cpp"><div class="toolbar"><span><a href="#" class="toolbar_item command_help help">?</a></span></div><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td class="code"><div class="container"><div class="line number1 index0 alt2"><code class="cpp keyword bold">template</code><code class="cpp plain"><</code><code class="cpp keyword bold">class</code> <code class="cpp plain">A, </code><code class="cpp keyword bold">class</code> <code class="cpp plain">B> pair(A, B) -> pair<A, B></code></div></div></td></tr></tbody></table></div></div></blockquote>
Deducing <code>(A, B)</code> from <code>(int, T)</code> yield <code>int</code> for <code>A</code>
and <code>T</code> for <code>B</code>. Now substitute back into the deduction guide to get
a new deduction guide
<blockquote><div><div id="highlighter_147664" class="syntaxhighlighter nogutter cpp"><div class="toolbar"><span><a href="#" class="toolbar_item command_help help">?</a></span></div><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td class="code"><div class="container"><div class="line number1 index0 alt2"><code class="cpp keyword bold">template</code><code class="cpp plain"><</code><code class="cpp keyword bold">class</code> <code class="cpp plain">T> pair(</code><code class="cpp color1 bold">int</code><code class="cpp plain">, T) -> pair<</code><code class="cpp color1 bold">int</code><code class="cpp plain">, T>;</code></div></div></td></tr></tbody></table></div></div></blockquote>
Deducing the template arguments for the alias template from this gives us the following deduction guide for the alias template
<blockquote><div><div id="highlighter_362756" class="syntaxhighlighter nogutter cpp"><div class="toolbar"><span><a href="#" class="toolbar_item command_help help">?</a></span></div><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td class="code"><div class="container"><div class="line number1 index0 alt2"><code class="cpp keyword bold">template</code><code class="cpp plain"><</code><code class="cpp keyword bold">class</code> <code class="cpp plain">T> P(</code><code class="cpp color1 bold">int</code><code class="cpp plain">, T) -> P<T>;</code></div></div></td></tr></tbody></table></div></div></blockquote>
A repository of additional expository materials and worked out examples used in the refinement of this algorithm
is maintained <a href="https://htmlpreview.github.io/?https://github.com/mspertus/CTAD_POST_CPP17/master/CTAD_alias_examples.html">online</a>.
<h2>Deducing from inherited constructors</h2>
In C++17, deduction guides (implicit and explicit) are not inherited when constructors are inherited.
According to the C++ Core Guidelines <a href="https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rc-inheriting">C.52</a>, you should “use inheriting constructors to import constructors into a derived class that does not need further explicit initialization”. As the creator of such a thin wrapper has not asked in any way for the derived class to behave differently under construction, our experience is that users are
surprised that construction behavior changes:
<table border="1"><tbody><tr><td colspan="2">
<div><div id="highlighter_36110" class="syntaxhighlighter nogutter cpp"><div class="toolbar"><span><a href="#" class="toolbar_item command_help help">?</a></span></div><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td class="code"><div class="container"><div class="line number1 index0 alt2"><code class="cpp keyword bold">template</code> <code class="cpp plain"><</code><code class="cpp keyword bold">typename</code> <code class="cpp plain">T> </code><code class="cpp keyword bold">struct</code> <code class="cpp plain">CallObserver requires Invocable<T> { </code></div><div class="line number2 index1 alt1"><code class="cpp spaces"> </code><code class="cpp plain">CallObserver(T &&) : t(std::forward<T>(t)) {}</code></div><div class="line number3 index2 alt2"><code class="cpp spaces"> </code><code class="cpp keyword bold">virtual</code> <code class="cpp keyword bold">void</code> <code class="cpp plain">observeCall() { t(); }</code></div><div class="line number4 index3 alt1"><code class="cpp spaces"> </code><code class="cpp plain">T t;</code></div><div class="line number5 index4 alt2"><code class="cpp plain">};</code></div><div class="line number6 index5 alt1"> </div><div class="line number7 index6 alt2"><code class="cpp keyword bold">template</code> <code class="cpp plain"><</code><code class="cpp keyword bold">typename</code> <code class="cpp plain">T> </code><code class="cpp keyword bold">struct</code> <code class="cpp plain">CallLogger : </code><code class="cpp keyword bold">public</code> <code class="cpp plain">CallObserver<T> { </code></div><div class="line number8 index7 alt1"><code class="cpp spaces"> </code><code class="cpp keyword bold">using</code> <code class="cpp plain">CallObserver<T>::CallObserver; </code></div><div class="line number9 index8 alt2"><code class="cpp spaces"> </code><code class="cpp keyword bold">virtual</code> <code class="cpp keyword bold">void</code> <code class="cpp plain">observeCall() override { cout << </code><code class="cpp string">"calling"</code><code class="cpp plain">; t(); }</code></div><div class="line number10 index9 alt1"><code class="cpp plain">};</code></div></div></td></tr></tbody></table></div></div>
</td></tr>
<tr><th>C++17</th> <th>Proposed</th></tr>
<tr><td>
<div><div id="highlighter_155720" class="syntaxhighlighter nogutter cpp"><div class="toolbar"><span><a href="#" class="toolbar_item command_help help">?</a></span></div><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td class="code"><div class="container"><div class="line number1 index0 alt2"><code class="cpp plain">CallObserver b([]() { </code><code class="cpp comments">/* ... */</code> <code class="cpp plain">}); </code><code class="cpp comments">// OK</code></div><div class="line number2 index1 alt1"><code class="cpp plain">CallLogger<</code><code class="cpp comments">/*????*/</code><code class="cpp plain">> d([]() { </code><code class="cpp comments">/* ... */</code> <code class="cpp plain">});</code></div></div></td></tr></tbody></table></div></div>
</td>
<td><div><div id="highlighter_787208" class="syntaxhighlighter nogutter cpp"><div class="toolbar"><span><a href="#" class="toolbar_item command_help help">?</a></span></div><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td class="code"><div class="container"><div class="line number1 index0 alt2"><code class="cpp plain">CallObserver b([]() { </code><code class="cpp comments">/* ... */</code> <code class="cpp plain">}); </code><code class="cpp comments">// OK</code></div><div class="line number2 index1 alt1"><code class="cpp plain">Derived d([]() { </code><code class="cpp comments">/* ... */</code> <code class="cpp plain">}); </code><code class="cpp comments">// Now OK</code></div></div></td></tr></tbody></table></div></div>
</td></tr></tbody></table>
Note that inheriting the constructors of a base class must include inheriting all the deduction guides, not just the implicit ones. As a number of standard library
writers use explicit guides to behave “as-if” their classes were defined as in the standard, such internal implementation details
details would become visible if only the internal guides were inherited. We of course use the same algorithm
for determining deduction guides for the base class template as described above for alias templates.
<h2>Class Template Argument Deduction and partial template argument lists</h2>
Partial template argument lists can be given in Function Template Argument Deduction
but surprisingly fail in Class Template Argument Deduction, a restriction we propose removing.
Not only is this more consistent, but it has powerful applications that resolve longstanding
difficulties in C++, such as creating associative containers with lambda comparators, as the following
example shows.
<table border="1"><tbody><tr><th>C++17</th><th>Proposed</th></tr>
<tr><td><div><div id="highlighter_644899" class="syntaxhighlighter nogutter cpp"><div class="toolbar"><span><a href="#" class="toolbar_item command_help help">?</a></span></div><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td class="code"><div class="container"><div class="line number1 index0 alt2"><code class="cpp keyword bold">using</code> <code class="cpp keyword bold">namespace</code> <code class="cpp plain">ba = boost::algorithm;</code></div><div class="line number2 index1 alt1"> </div><div class="line number3 index2 alt2"><code class="cpp plain">set<string, ba::ilexicographic_compare> case_insensitive_strings(ba::ilexicographic_compare);</code></div><div class="line number4 index3 alt1"> </div><div class="line number5 index4 alt2"><code class="cpp comments">// Lambda comparators are great for algorithms like sort</code></div><div class="line number6 index5 alt1"><code class="cpp comments">// Why can't we use them for associative containers?</code></div><div class="line number7 index6 alt2"><code class="cpp plain">set<</code><code class="cpp color1 bold">int</code><code class="cpp plain">, </code><code class="cpp comments">/* ???? */</code><code class="cpp plain">> s([](</code><code class="cpp color1 bold">int</code> <code class="cpp plain">i, </code><code class="cpp color1 bold">int</code> <code class="cpp plain">j) { </code></div><div class="line number8 index7 alt1"><code class="cpp spaces"> </code><code class="cpp keyword bold">return</code> <code class="cpp plain">std::popcount(i) < std::popcount(j); </code></div><div class="line number9 index8 alt2"><code class="cpp spaces"> </code><code class="cpp plain">}); </code></div><div class="line number10 index9 alt1"> </div><div class="line number11 index10 alt2"><code class="cpp comments">// Or container adaptors?</code></div><div class="line number12 index11 alt1"><code class="cpp plain">priority_queue<Task, vector<Task>, </code><code class="cpp comments">/* ???? */</code><code class="cpp plain">> tasks([](Task a, Task b) {</code></div><div class="line number13 index12 alt2"><code class="cpp spaces"> </code><code class="cpp keyword bold">return</code> <code class="cpp plain">a.priority < b.priority;</code></div><div class="line number14 index13 alt1"><code class="cpp spaces"> </code><code class="cpp plain">});</code></div></div></td></tr></tbody></table></div></div>
</td>
<td><div><div id="highlighter_883" class="syntaxhighlighter nogutter cpp"><div class="toolbar"><span><a href="#" class="toolbar_item command_help help">?</a></span></div><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td class="code"><div class="container"><div class="line number1 index0 alt2"><code class="cpp keyword bold">using</code> <code class="cpp keyword bold">namespace</code> <code class="cpp plain">ba = boost::algorithm;</code></div><div class="line number2 index1 alt1"> </div><div class="line number3 index2 alt2"><code class="cpp plain">set<string> case_insensitive_strings(ba::ilexicographic_compare);</code></div><div class="line number4 index3 alt1"> </div><div class="line number5 index4 alt2"><code class="cpp comments">// Lambda comparators are great for algorithms like sort</code></div><div class="line number6 index5 alt1"><code class="cpp comments">// Now with associative containers, too!</code></div><div class="line number7 index6 alt2"><code class="cpp plain">set<</code><code class="cpp color1 bold">int</code><code class="cpp plain">> s([](</code><code class="cpp color1 bold">int</code> <code class="cpp plain">i, </code><code class="cpp color1 bold">int</code> <code class="cpp plain">j) { </code></div><div class="line number8 index7 alt1"><code class="cpp spaces"> </code><code class="cpp keyword bold">return</code> <code class="cpp plain">std::popcount(i) < std::popcount(j);</code></div><div class="line number9 index8 alt2"><code class="cpp spaces"> </code><code class="cpp plain">}); </code></div><div class="line number10 index9 alt1"> </div><div class="line number11 index10 alt2"><code class="cpp comments">// and container adaptors!</code></div><div class="line number12 index11 alt1"><code class="cpp plain">priority_queue<Task> tasks([](Task a, Task b) { </code></div><div class="line number13 index12 alt2"><code class="cpp spaces"> </code><code class="cpp keyword bold">return</code> <code class="cpp plain">a.priority < b.priority;</code></div><div class="line number14 index13 alt1"><code class="cpp spaces"> </code><code class="cpp plain">});</code></div></div></td></tr></tbody></table></div></div></td></tr></tbody></table>
<h3>Technical considerations</h3>
The above discussion naturally begs the question as to why partial template
argument lists in Class Template Argument Discussion were not supported from the beginning. This feature
was deferred until after C++17 to give further time to analyze the possibility of breaking changes when partially
template argument lists were combined with default arguments or variadic templates
as discussed below.
<h4>Default arguments</h4>
Consider the expression
<blockquote><div><div id="highlighter_98799" class="syntaxhighlighter nogutter cpp"><div class="toolbar"><span><a href="#" class="toolbar_item command_help help">?</a></span></div><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td class="code"><div class="container"><div class="line number1 index0 alt2"><code class="cpp plain">vector<</code><code class="cpp color1 bold">int</code><code class="cpp plain">>(MyAlloc())</code></div></div></td></tr></tbody></table></div></div></blockquote>
As far back as C++98, this creates a <tt>vector<int, allocator<int>></tt>,
although it is unlikely to compile because <tt>MyAlloc</tt> almost certainly is not
convertible to <tt>allocator<int></tt>. By contrast, ordinary function template
argument deduction would of course deduce <tt>vector<int, MyAlloc></tt>.
While this is exactly what we would want in this situation and is likely
what we would have done if Class Template Argument Deduction was in C++ from the
beginning, we would now have to contend with it being a breaking change.
<p>Fortunately, we can get the benefit of the usual Function Template Argument Deduction without breaking existing code by the following rule:
</p><ul><li>If a class has no explicit deduction guides, we propose that C++17 behavior applies where default template
arguments override type inference. If the class has explicit
guides, then inference overrides default arguments as in normal Function Template Argument Deduction.</li></ul>
This maintains the behavior of legacy C++ for classes that
were designed before Class Template Argument Deduction was introduced, while at the same time allowing
important examples like the above to be handled correctly (assuming <a href="https://wg21.link/p1069r0">P1069R0</a>, which we believe is certainly desirable. Of course, once a class has
deduction guides, nearly any desired behavior can be specified, so other examples should be handleable as needed.<p>
<b>Notes:</b></p><ul><li>As this rule is only relevant for template parameters with default initializers. For class template
parameters without default initializers, the programmer can ignore it.</li>
<li>Without this rule, no combination of traditional deduction guides could produce this behavior, severely
limiting the ability to use deduction guides to customize class behavior.</li>
<li>If deduction guides are ever supported for function templates in addition to class templates as
proposed in <a href="http://wg21.link/p1168r0">P1168R0</a>,
having deduction guides follow the usual rules for function template argument deduction in which inference overrides default values
seems clearly the right approach. This proposal will be consistent with that.</li></ul>
<h4>Variadic arguments</h4>
<p>One other technical issue discussed during C++17 was around variadic templates. In particular, what should <tt>tuple<int>(1, 2.3)</tt> deduce?
Again, the C++17 behavior of <tt>tuple<int></tt> is incorrect, as such templates have the wrong number of arguments. While it
is tempting to deduce additional arguments just like <tt>make_tuple<int>(1, 2.3)</tt> does, we do not propose at this time allowing additional
variadic arguments to be deduced for the following reasons:
</p><ul><li>The following code is both valid and useful in C++17
<blockquote><div><div id="highlighter_281401" class="syntaxhighlighter nogutter cpp"><div class="toolbar"><span><a href="#" class="toolbar_item command_help help">?</a></span></div><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td class="code"><div class="container"><div class="line number1 index0 alt2"><code class="cpp plain">tuple<</code><code class="cpp color1 bold">int</code><code class="cpp plain">> t(5, allocator<</code><code class="cpp color1 bold">int</code><code class="cpp plain">>());</code></div></div></td></tr></tbody></table></div></div></blockquote>
However, if additional variadic arguments were deduced, this code would erroneously deduce <tt>tuple<int, allocator<int>></tt>,
introducing a breaking change.</li>
<li>Since programmers are used to thinking of <tt>tuple<int></tt> as the name of a type, allowing it to
deduce to the very different type tuple<int, allocator<int>></li> might well be confusing.
<li>We are not aware of any truly compelling use cases for deducing extra variadic arguments on
a par with the ones listed above, so the downsides outweigh the benefits in our opinion.</li></ul>
<b>Note:</b>
<blockquote>If at some point in the future it is desired to align more closely with function templates
by allowing additional variadic arguments to be deduced, it may become possible with other related language changes.
For example, if the right-hand side of a deduction guide could be a <em>type-id</em> instead of just
a <em>simple-template-id</em>, a metafunction could compute the deduce different <tt>tuple</tt> types based on
whether the last argument is an allocator as well as unrelated use cases such as Richard Smith's delayed forwarding example
in P1021R0, but as there is absolutely no requirement that this be considered for C++20, so we do not pursue this
any further at this time.</blockquote>
<script type="text/javascript">
SyntaxHighlighter.defaults['gutter'] = false;
SyntaxHighlighter.all()
</script>
</body></html>