-
Notifications
You must be signed in to change notification settings - Fork 0
/
p1715.html
208 lines (164 loc) · 8.06 KB
/
p1715.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
<html>
<head>
<title>Loosen restrictions on "_t" typedefs. </title>
<meta Author="Jorg Brown" content="proposal">
</head>
<body bgcolor="#FFFFFF" text="#000000">
<style>
del { background-color:LightPink; }
ins { background-color:LightGreen; }
</style>
<font size=-1>
Jorg Brown <[email protected]>
<br>
2023-02-06
<br>
Document P1715R1
<code>$Id: proposal.html,v 1.50 2023/02/06 11:50:21 jorg Exp $</code>
</font>
<h1>P1715R1
(Revision 1)</h1>
<h2>Revision history</h2>
<ul>
<li>2023-02-06: R1: Proposal to loosen restrictions on "_t" typedefs.</li>
<ul>
<li>Don't remove restrictions on those typedefs that could be implemented directly, for example std::add_const_t, since a direct implementation would allow for type deduction to occur and thus remove the need to explicitly specify a template type parameter.</li>
<li>Update section numbers for c++23.</li>
</ul>
<li>2019-06-17: R0: Proposal to loosen restrictions on "_t" typedefs.</li>
</ul>
<h2>Introduction.</h2>
<p>
In C++14, templated typedefs and values were added alongside similarly-named templated classes. For example:
</p>
<blockquote>
<pre>
// C++11:
// "If B is true, the member typedef type shall equal T. If B is false, the member typedef type shall equal F."
template<bool b, class T, class F>
struct conditional;
// C++14
template<bool b, class T, class F>
using conditional_t = typename conditional<b, T, F>::type;
</pre>
</blockquote>
<p>
Unfortunately, this is all the standard has to say about conditional_t. In particular, the standard does not leave implementors the option of defining conditional in terms of conditional_t.
</p>
<h2>Motivation and Scope.</h2>
<p>
This definition of conditional_t requires that the compiler instantiate the templated class conditional for each combination of b/T/F template parameters. This has measurable impact on both compile time, and size of debug information, especially in environments where template meta-programming is in heavy use.
</p>
<p>
Consider an alternative implementation of conditional_t:
</p>
<blockquote>
<pre>
template<bool _Bp> struct __select;
template<> struct __select<true> { template<typename _TrueT, typename _FalseT> using type = _TrueT;};
template<> struct __select<false> { template<typename _TrueT, typename _FalseT> using type = _FalseT;};
template <bool _Bp, class _TrueT, class _FalseT> using conditional_t = typename __select<_Bp>::template type<_TrueT, _FalseT>;
</pre>
</blockquote>
<p>
In this implementation, no matter how many times conditional_t is used, there are only two types that are instantiated as a result: __select<true> and __select<false>.
</p>
<p>
This not only saves in compiler memory, but also debug information. Here at Google, for a particularly TMP-heavy file, it turned out that around 1/6th of all classes emitted as part of clang's debug information were instantiations of std::conditional.
</p>
<p>While proposing a change to fix this, and thus reduce debug size, it was pointed out that this change is actually observable. Suppose a function is declared in a header like this:
<blockquote>
<pre>
template<bool B> long to_long(conditional_t<B, int, long> param);
</pre>
</blockquote>
and then later in the same file, implemented like this, presumably because the programmer accidentally updated only one of two mentions of to_long:
<blockquote>
<pre>
template<bool B> long to_long(typename conditional<B, int, long>::type param) {
return param;
}
</pre>
</blockquote>
</p>
<p>
The header compiles just fine but if conditional_t has not been defined in terms of conditional, then it turns out we have defined two different overloads. Then when to_long is called, we will get a "call is ambiguous" error. ( See https://godbolt.org/z/tEH-pq )
</p>
<p>
Despite this theoretical impact, the actual impact is quite low; thankfully programmers almost never declare a function in terms of one templated type, and then define it with another.
</p>
<h2>Impact on the Standard</h2>
<p>
This proposal suggests that the while the type produced by the templated name_t variants of templated name_t classes must be identical, they are free to arrive at that type in any way the library author chooses.
</p>
<h2>Proposed Wording</h2>
<p>Note: All changes are relative to the 2023-01-04 working draft of C++23.</p>
<p>Modify 22.4.2 [tuple.syn] :</p>
<blockquote>
<pre>
template<size_t I, class T>
using tuple_element_t = <ins>/* produces same type as </ins>typename tuple_element<I, T>::type <ins>*/</ins>;
</pre>
</blockquote>
<p>Modify 22.6.2 [variant.syn] :</p>
<blockquote>
<pre>
template<size_t I, class T>
using variant_alternative_t = <ins>/* produces same type as </ins>typename variant_alternative<I, T>::type <ins>*/</ins>;
</pre>
</blockquote>
<p>Modify 22.10.2 [functional.syn] :</p>
<blockquote>
<pre>
template<class T> using unwrap_ref_decay_t = <ins>/* produces same type as </ins>typename unwrap_ref_decay<T>::type <ins>*/</ins>;
</pre>
</blockquote>
<p>Modify 21.3.3 [meta.type.synop] :</p>
<blockquote>
<pre>
template<class T>
using remove_const_t = <ins>/* produces same type as </ins>typename remove_const<T>::type <ins>*/</ins>;
template<class T>
using remove_volatile_t = <ins>/* produces same type as </ins>typename remove_volatile<T>::type <ins>*/</ins>;
template<class T>
using remove_cv_t = <ins>/* produces same type as </ins>typename remove_cv<T>::type <ins>*/</ins>;
template<class T>
using remove_reference_t = <ins>/* produces same type as </ins>typename remove_reference<T>::type <ins>*/</ins>;
template<class T>
using make_signed_t = <ins>/* produces same type as </ins>typename make_signed<T>::type <ins>*/</ins>;
template<class T>
using make_unsigned_t = <ins>/* produces same type as </ins>typename make_unsigned<T>::type <ins>*/</ins>;
template<class T>
using remove_extent_t = <ins>/* produces same type as </ins>typename remove_extent<T>::type <ins>*/</ins>;
template<class T>
using remove_all_extents_t = <ins>/* produces same type as </ins>typename remove_all_extents<T>::type <ins>*/</ins>;
template<class T>
using remove_pointer_t = <ins>/* produces same type as </ins>typename remove_pointer<T>::type <ins>*/</ins>;
template<size_t Len, size_t Align = default-alignment> // see [meta.trans.other]
using aligned_storage_t = <ins>/* produces same type as </ins>typename aligned_storage<Len, Align>::type <ins>*/</ins>;
template<size_t Len, class... Types>
using aligned_union_t = <ins>/* produces same type as </ins>typename aligned_union<Len, Types...>::type <ins>*/</ins>;
template<class T>
using remove_cvref_t = <ins>/* produces same type as </ins>typename remove_cvref<T>::type <ins>*/</ins>;
template<class T>
using decay_t = <ins>/* produces same type as </ins>typename decay<T>::type <ins>*/</ins>;
template<bool b, class T = void>
using enable_if_t = <ins>/* produces same type as </ins>typename enable_if<b, T>::type <ins>*/</ins>;
template<bool b, class T, class F>
using conditional_t = <ins>/* produces same type as </ins>typename conditional<b, T, F>::type <ins>*/</ins>;
template<class... T>
using common_type_t = <ins>/* produces same type as </ins>typename common_type<T...>::type <ins>*/</ins>;
template<class... T>
using common_reference_t = <ins>/* produces same type as </ins>typename common_reference<T...>::type <ins>*/</ins>;
template<class T>
using underlying_type_t = <ins>/* produces same type as </ins>typename underlying_type<T>::type <ins>*/</ins>;
template<class Fn, class... ArgTypes>
using invoke_result_t = <ins>/* produces same type as </ins>typename invoke_result<Fn, ArgTypes...>::type <ins>*/</ins>;
</pre>
</blockquote>
<h2>Last words</h2>
<p>
The author is unaware of observable impact of changing the "_v" constexpr definitions, such as tuple_size_v, to be computed in some other way, so no proposal is made to change them.
</p>
</body>
</html>