-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathp0674r1.html
319 lines (319 loc) · 16.3 KB
/
p0674r1.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
<!DOCTYPE html>
<html lang="en">
<head>
<title>Extending make_shared to support arrays</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
ins { background-color: #CCFFCC; }
.replace { background-color: #F0F0F0; }
</style>
</head>
<body>
<p>
<strong>Document Number:</strong> P0674R1<br>
<strong>Date:</strong> 2017-07-12<br>
<strong>Project:</strong> Programming Language C++<br>
<strong>Audience:</strong> Library Evolution Working Group<br>
<strong>Authors:</strong> Peter Dimov
(<a href="mailto:[email protected]">[email protected]</a>),
Glen Fernandes
(<a href="mailto:[email protected]">[email protected]</a>)
</p>
<h1>Extending make_shared to support arrays</h1>
<p>This paper proposes adding array support to <code>make_shared</code> and
<code>allocate_shared</code>, via the syntax
<code>make_shared<T[]></code> and
<code>make_shared<T[N]></code>.</p>
<h2>Revision History</h2>
<h3>Changes in 0674R1</h3>
<p>This revision of P0674R0 replaces the use of <em>initialized to</em> and
<em>value-initialized</em> to avoid conflict with the core language terms.</p>
<h3>Changes in Revision 3</h3>
<p>This revision of N3939 removes the <code>_noinit</code> overloads, performs
some slight modifications to the proposed text, and adds a wording rationale
section.</p>
<h3>Changes in Revision 2</h3>
<p>This revision of N3870 removes the scalar <code>T&&</code>
overloads, reflecting the result of the LEWG review.</p>
<h3>Changes in Revision 1</h3>
<p>This revision of N3641 significantly narrows the scope of the proposal,
based on feedback from Jeffrey Yasskin and Stephan T. Lavavej. It cuts down
the number of overloads considerably, leaving only two use cases:</p>
<ul>
<li>Value initialization, analogous to <code>new U[N]()</code>:
<blockquote>
<p><code>// <em>if T is an array type of form U[]</em><br>
template<class T> shared_ptr<T> make_shared(size_t N);</code></p>
<p><code>// <em>if T is an array type of form T[N]</em><br>
template<class T> shared_ptr<T> make_shared();</code></p>
</blockquote></li>
<li>Per-element initialization to a specified value, analogous to the
<code>std::vector<U>(N, v)</code> constructor:
<blockquote>
<p><code>// <em>if T is an array type of form U[]</em><br>
template<class T> shared_ptr<T>
make_shared(size_t N, const remove_extent_t<T>& u);</code></p>
<p><code>// <em>if T is an array type of form T[N]</em><br>
template<class T> shared_ptr<T>
make_shared(const remove_extent_t<T>& u);</code></p>
</blockquote></li>
</ul>
<h2>Motivation</h2>
<p><code>make_shared</code> is consistently recommended as the preferred way
to create objects managed by <code>shared_ptr</code>. It delivers exception
safety, is easier to type, and saves one allocation (and a few bytes in the
control block). Not surprisingly, a very common request is for it to support
arrays.</p>
<p>An implementation of this proposal has been available from
<a href="http://www.boost.org">Boost</a> since release 1.56 and is in wide
use.</p>
<p>The proposed wording also resolves library issue #2070.</p>
<h2>Wording Rationale</h2>
<p>The proposed text contains what some may consider redefinitions of the
standard terms <em>initialized to</em> and <em>value-initialized</em>, and a
lot of seemingly unnecessary complexity.</p>
<p>This is all needed in order to properly initialize arrays due to the
irregular behavior of the standard <em>new-expression</em>. When the type
<code>T</code> in the expression <code>::new (pv) T()</code> is an array type,
what happens is not, as in the scalar case, an object of type <code>T</code>
being constructed at the address <code>pv</code>. When <code>T</code> is for
instance <code>U[5]</code>, the expression turns into the array form of
<code>new</code>, which means that it calls <code>operator new[]</code>
instead and may prepend a length prefix to the array elements. The end result
is that code cannot assume that after the <em>new-expression</em>
<code>pv</code> points to an object of type <code>T</code>, that is,
<code>U[5]</code> — because it may not.</p>
<p>For this reason, the wording below does not define initialization as just
<code>::new T</code>. Instead, the array initializations are specifically
redefined to consist of initializations of every element. Only for scalar
types does the wording invoke <code>::new T</code> (or <code>construct</code>
in the allocator case.)</p>
<h2>Proposed Text</h2>
<p>(All edits are relative to N4659.)</p>
<p>Change 23.11.2.2 [util.smartptr.shared] p1 as follows:</p>
<blockquote>
<p><code>// <em>23.11.2.2.6, shared_ptr creation</em><br>
<ins>// <em>if T is not an array type</em></ins><br>
template<class T, class... Args> shared_ptr<T>
make_shared(Args&&... args);<br>
template<class T, class A, class... Args> shared_ptr<T>
allocate_shared(const A& a, Args&&... args);</code></p>
<p><ins><code>// <em>if T is an array type of form U[]</em><br>
template<class T> shared_ptr<T> make_shared(size_t N);<br>
template<class T, class A> shared_ptr<T>
allocate_shared(const A& a, size_t N);</code></ins></p>
<p><ins><code>// <em>if T is an array type of form U[N]</em><br>
template<class T> shared_ptr<T> make_shared();<br>
template<class T, class A> shared_ptr<T>
allocate_shared(const A& a);</code></ins></p>
<p><ins><code>// <em>if T is an array type of form U[]</em><br>
template<class T> shared_ptr<T>
make_shared(size_t N, const remove_extent_t<T>& u);<br>
template<class T, class A> shared_ptr<T>
allocate_shared(const A& a, size_t N,
const remove_extent_t<T>& u);</code></ins></p>
<p><ins><code>// <em>if T is an array type of form U[N]</em><br>
template<class T> shared_ptr<T>
make_shared(const remove_extent_t<T>& v);<br>
template<class T, class A> shared_ptr<T>
allocate_shared(const A& a,
const remove_extent_t<T>& v);</code></ins></p>
</blockquote>
<p>Replace the contents 23.11.2.2.6 [util.smartptr.shared.create] with the
following:</p>
<blockquote class="replace">
<p>The common requirements that apply to all <code>make_shared</code> and
<code>allocate_shared</code> overloads, unless specified otherwise, are
described below.</p>
<dl>
<dt><code>template<class T, <em>see below</em>> shared_ptr<T>
make_shared(<em>args</em>);<br>
template<class T, class A, <em>see below</em>> shared_ptr<T>
allocate_shared(const A& a, <em>args</em>);</code></dt>
<dd>
<p><em>Requires:</em> <code>A</code> shall be an allocator (20.5.3.5). The
copy constructor, move constructor and destructor of <code>A</code> shall not
throw exceptions.</p>
<p><em>Effects:</em> Allocates memory for an object of type <code>T</code> (or
<code>U[N]</code> when <code>T</code> is <code>U[]</code>,
where <code>N</code> is determined from <code>args</code> as specified by the
concrete overload). The object is initialized from <code><em>args</em></code>
as specified by the concrete overload. The <code>allocate_shared</code>
templates use a copy of <code>a</code> (rebound for an unspecified
<code>value_type</code>) to allocate memory. If an exception is thrown, the
functions have no effect.</p>
<p><em>Returns:</em> A <code>shared_ptr</code> instance that stores and
<em>owns</em> the address of the newly constructed object.</p>
<p><em>Postconditions:</em>
<code>r.get() != 0 && r.use_count() == 1</code>, where <code>r</code>
is the return value.</p>
<p><em>Throws:</em> <code>bad_alloc</code>, an exception thrown from
<code>allocate</code>, or from the initialization of the object.</p>
<p><em>Remarks:</em>
<ul>
<li>Implementations should perform no more than one memory allocation. [
<em>Note:</em> This provides efficiency equivalent to an intrusive smart
pointer. — <em>end note</em> ].
<li>When an object of an array type <code>U</code> is specified to have an
<em>initial value</em> of <code>u</code> (of the same type), this shall be
interpreted to mean that each array element of the object has as its
<em>initial value</em> the corresponding element from <code>u</code>.</li>
<li>When an object of an array type is specified to have a <em>default initial
value</em>, this shall be interpreted to mean that each array
element of the object has a <em>default initial value</em>.</li>
<li>When a (sub)object of a non-array type <code>U</code> is specified to have
an <em>initial value</em> of <code>v</code>, or <code>U(l...)</code>, where
<code>l...</code> is a list of constructor arguments, <code>make_shared</code>
shall initialize this (sub)object via the expression
<code>::new(pv) U(v)</code> or <code>::new(pv) U(l...)</code> respectively,
where <code>pv</code> has type <code>void*</code> and points to storage
suitable to hold an object of type <code>U</code>.</li>
<li>When a (sub)object of a non-array type <code>U</code> is specified to have
an <em>initial value</em> of <code>v</code>, or <code>U(l...)</code>, where
<code>l...</code> is a list of constructor arguments,
<code>allocate_shared</code> shall initialize this (sub)object via the
expression <code>allocator_traits<A2>::construct(a2, pv, v)</code> or
<code>allocator_traits<A2>::construct(a2, pv, l...)</code> respectively,
where <code>pv</code> points to storage suitable to hold an object of type
<code>U</code> and <code>a2</code> of type <code>A2</code> is a rebound copy
of the allocator <code>a</code> passed to <code>allocate_shared</code> such
that its <code>value_type</code> is <code>U</code>.</li>
<li>When a (sub)object of non-array type <code>U</code> is specified to have a
<em>default initial value</em>, <code>make_shared</code> shall initialize this
(sub)object via the expression <code>::new(pv) U()</code>, where
<code>pv</code> has type <code>void*</code> and points to storage suitable to
hold an object of type <code>U</code>.</li>
<li>When a (sub)object of non-array type <code>U</code> is specified to have a
<em>default initial value</em>, <code>allocate_shared</code> shall initialize
this (sub)object via the expression
<code>allocator_traits<A2>::construct(a2, pv)</code>, where
<code>pv</code> points to storage suitable to hold an object of type
<code>U</code> and <code>a2</code> of type <code>A2</code> is a rebound copy
of the allocator <code>a</code> passed to <code>allocate_shared</code> such
that its <code>value_type</code> is <code>U</code>.</li>
<li>Array elements are initialized in ascending order of their addresses.</li>
<li>When the lifetime of the object managed by the return value ends, or when
the initialization of an array element throws an exception, the initialized
elements should be destroyed in the reverse order of their construction.</li>
</ul>
<p>[ <em>Note:</em> These functions will typically allocate more memory than
the size of the element objects to allow for internal bookkeeping structures
such as the reference counts. — <em>end note</em> ]</p>
</dd>
</dl>
<dl>
<dt><code>// <em>if T is not an array type</em><br>
template<class T, class... Args> shared_ptr<T>
make_shared(Args&&... args);<br>
template<class T, class A, class... Args> shared_ptr<T>
allocate_shared(const A& a, Args&&... args);</code></dt>
<dd>
<p><em>Returns:</em> A <code>shared_ptr</code> to an object of type
<code>T</code> with an <em>initial value</em>
<code>T(forward<Args>(args)...)</code>.</p>
<p><em>Remarks:</em> These overloads shall only participate in overload
resolution when <code>T</code> is not an array type. The
<code>shared_ptr</code> constructors called by these functions enable
<code>shared_from_this</code> with the address of the newly constructed object
of type <code>T</code>.</p>
<p>[ <em>Example:</em></p>
<p><code>// <em>shared_ptr to int()</em><br>
shared_ptr<int> p = make_shared<int>();</code></p>
<p><code>// <em>shared_ptr to vector of 16 elements with value 1</em><br>
shared_ptr<vector<int> > q =
make_shared<vector<int> >(16, 1);</code></p>
<p>— <em>end example</em> ]</p>
</dd>
<dt><code>// <em>if T is an array type of form U[]</em><br>
template<class T> shared_ptr<T> make_shared(size_t N);<br>
template<class T, class A> shared_ptr<T>
allocate_shared(const A& a, size_t N);</code></dt>
<dd>
<p><em>Returns:</em> A shared_ptr to an object of type <code>U[N]</code> with
a <em>default initial value</em>, where <code>U</code> is
<code>remove_extent_t<T></code>.</p>
<p><em>Remarks:</em> These overloads shall only participate in overload
resolution when <code>T</code> is an array type of form <code>U[]</code>.</p>
<p>[ <em>Example:</em></p>
<p><code>// <em>shared_ptr to a value-initialized double[1024]</em><br>
shared_ptr<double[]> p = make_shared<double[]>(1024);</code></p>
<p><code>// <em>shared_ptr to a value-initialized double[6][2][2]</em><br>
shared_ptr<double[][2][2]> q =
make_shared<double[][2][2]>(6);</code></p>
<p>— <em>end example</em> ]</p>
</dd>
<dt><code>// <em>if T is an array type of form U[N]</em><br>
template<class T> shared_ptr<T> make_shared();<br>
template<class T, class A> shared_ptr<T>
allocate_shared(const A& a);</code></dt>
<dd>
<p><em>Returns:</em> A shared_ptr to an object of type <code>T</code> with a
<em>default initial value</em>.</p>
<p><em>Remarks:</em> These overloads shall only participate in overload
resolution when <code>T</code> is an array type of form <code>U[N]</code>.</p>
<p>[ <em>Example:</em></p>
<p><code>// <em>shared_ptr to a value-initialized double[1024]</em><br>
shared_ptr<double> p = make_shared<double[1024]>();</code></p>
<p><code>// <em>shared_ptr to a value-initialized double[6][2][2]</em><br>
shared_ptr<double[6][2][2]> q =
make_shared<double[6][2][2]>();</code></p>
<p>— <em>end example</em> ]</p>
</dd>
<dt><code>// <em>if T is an array type of form U[]</em><br>
template<class T> shared_ptr<T> make_shared(size_t N,
const remove_extent_t<T>& u);<br>
template<class T, class A> shared_ptr<T>
allocate_shared(const A& a, size_t N,
const remove_extent_t<T>& u);</code></dt>
<dd>
<p><em>Returns:</em> A shared_ptr to an object of type <code>U[N]</code>,
where <code>U</code> is <code>remove_extent_t<T></code> and each array
element has an <em>initial value</em> of <code>u</code>.</p>
<p><em>Remarks:</em> These overloads shall only participate in overload
resolution when <code>T</code> is an array type of form <code>U[]</code>.</p>
<p>[ <em>Example:</em></p>
<p><code>// <em>shared_ptr to a double[1024], where each element is
1.0</em><br>
shared_ptr<double[]> p = make_shared<double[]>(1024,
1.0);</code></p>
<p><code>// <em>shared_ptr to a double[6][2] where each double[2] element is
{1.0, 0.0}</em><br>
shared_ptr<double[][2]> q =
make_shared<double[][2]>(6, {1.0, 0.0});</code></p>
<p><code>// <em>shared_ptr to a vector<int>[4] where each vector has
contents {1, 2}</em><br>
shared_ptr<vector<int>[]> r =
make_shared<vector<int>[]>(4, {1, 2});</code></p>
<p>— <em>end example</em> ]</p>
</dd>
<dt><code>// <em>if T is an array type of form U[N]</em><br>
template<class T> shared_ptr<T> make_shared(const
remove_extent_t<T>& u);<br>
template<class T, class A> shared_ptr<T>
allocate_shared(const A& a,
const remove_extent_t<T>& u);</code></dt>
<dd>
<p><em>Returns:</em> A shared_ptr to an object of type <code>T</code>,
where each array element has an <em>initial value</em> of <code>u</code>.</p>
<p><em>Remarks:</em> These overloads shall only participate in overload
resolution when <code>T</code> is an array type of form <code>U[N]</code>.</p>
<p>[ <em>Example:</em></p>
<p><code>// <em>shared_ptr to a double[1024], where each element is
1.0</em><br>
shared_ptr<double[1024]> p =
make_shared<double[1024]>(1.0);</code></p>
<p><code>// <em>shared_ptr to a double[6][2] where each double[2] element is
{1.0, 0.0}</em><br>
shared_ptr<double[6][2]> q =
make_shared<double[6][2]>({1.0, 0.0});</code></p>
<p><code>// <em>shared_ptr to a vector<int>[4] where each vector has
contents {1, 2}</em><br>
shared_ptr<vector<int>[4]> r =
make_shared<vector<int>[4]>({1, 2});</code></p>
<p>— <em>end example</em> ]</p>
</dd>
</dl>
</blockquote>
</body>
</html>