-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathincomplete.html
198 lines (169 loc) · 4.96 KB
/
incomplete.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
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Incomplete Types</title>
</head>
<body>
<address align=right>
<a href="mailto:howard.hinnant@gmail.com">Howard Hinnant</a><br>
2011-05-27
</address>
<hr>
<h1 align="center">Incomplete types and <tt>shared_ptr</tt> / <tt>unique_ptr</tt></h1>
<p>
Most templates in the C++ standard library require that they be instantiated
with complete types. However <tt>shared_ptr</tt> and <tt>unique_ptr</tt> are
<em>partial</em> exceptions. Some, but not all of their members can be
instantiated with incomplete types. The motivation for this is to support idioms
such as
<a href="http://en.wikipedia.org/wiki/Opaque_pointer#C.2B.2B"><i>pimpl</i></a>
using smart pointers, and without risking undefined behavior.
</p>
<p>
Undefined behavior can occur when you have an incomplete type and you call
<tt>delete</tt> on it:
</p>
<blockquote><pre>
class A;
A* a = ...;
delete a;
</pre></blockquote>
<p>
The above is legal code. It will compile. Your compiler may or may not emit
a warning for above code like the above. When it executes, bad things will
probably happen. If you're very lucky your program will crash. However a more
probable outcome is that your program will silently leak memory as <tt>~A()</tt>
won't be called.
</p>
<p>
Using <tt>auto_ptr<A></tt> in the above example doesn't help. You still
get the same undefined behavior as if you had used a raw pointer.
</p>
<p>
Nevertheless, using incomplete classes in certain places is <em>very</em>
useful! This is where <tt>shared_ptr</tt> and <tt>unique_ptr</tt> help. Use
of one of these smart pointers will let you get away with an incomplete type,
except where it is neccessary to have a complete type. And most importantly,
when it <em>is</em> necessary to have a complete type, you get a compile-time
error if you try to use the smart pointer with an incomplete type at that point.
</p>
<blockquote>
<p><b>
No more undefined behavior:
</b></p>
</blockquote>
<p>
If your code compiles, then you've used a complete type everywhere you need to.
</p>
<blockquote><pre>
class A
{
class impl;
std::unique_ptr<impl> ptr_; <font color="#C80000">// ok!</font>
public:
A();
~A();
// ...
};
</pre></blockquote>
<p>
<tt>shared_ptr</tt> and <tt>unique_ptr</tt> require a complete type in different
places. The reasons are obscure, having to do with a dynamic deleter vs a
static deleter. The precise reasons aren't important. In fact, in most code
it isn't really important for you to know exactly where a complete type is
required. Just code, and if you get it wrong, the compiler will tell you.
</p>
<p>
However, in case it is helpful to you, here is a table which documents several
members of <tt>shared_ptr</tt> and <tt>unique_ptr</tt> with respect to
completeness requirements. If the member requires a complete type, then entry
has a "C", otherwise the table entry is filled with "I". I've also added a
column for <tt>noexcept</tt> (True or False) for each operation.
</p>
<blockquote>
<table border="1" cellpadding=5>
<caption>Complete type rquirements for <tt>unique_ptr</tt> and <tt>shared_ptr</tt></caption>
<tr>
<th></th>
<th colspan=2><tt>unique_ptr</tt></th>
<th colspan=2><tt>shared_ptr</tt></th>
</tr>
<tr>
<th></th>
<th>I/C</th>
<th><tt>noexcept</tt></th>
<th>I/C</th>
<th><tt>noexcept</tt></th>
</tr>
<tr>
<th><tt>P()</tt><br/>default constructor</th>
<td align="center">I</td>
<td align="center">T</td>
<td align="center">I</td>
<td align="center">T</td>
</tr>
<tr>
<th><tt>P(const P&)</tt><br/>copy constructor</th>
<td align="center">N/A</td>
<td align="center">N/A</td>
<td align="center">I</td>
<td align="center">T</td>
</tr>
<tr>
<th><tt>P(P&&)</tt><br/>move constructor</th>
<td align="center">I</td>
<td align="center">T</td>
<td align="center">I</td>
<td align="center">T</td>
</tr>
<tr>
<th><tt>~P()</tt><br/>destructor</th>
<td align="center">C</td>
<td align="center">T</td>
<td align="center">I</td>
<td align="center">T</td>
</tr>
<tr>
<th><tt>P(A*)</tt><br/></th>
<td align="center">I</td>
<td align="center">T</td>
<td align="center">C</td>
<td align="center">F</td>
</tr>
<tr>
<th><tt>operator=(const P&)</tt><br/>copy assignment</th>
<td align="center">N/A</td>
<td align="center">N/A</td>
<td align="center">I</td>
<td align="center">T</td>
</tr>
<tr>
<th><tt>operator=(P&&)</tt><br/>move assignment</th>
<td align="center">C</td>
<td align="center">T</td>
<td align="center">I</td>
<td align="center">T</td>
</tr>
<tr>
<th><tt>reset()</tt><br/></th>
<td align="center">C</td>
<td align="center">T</td>
<td align="center">I</td>
<td align="center">T</td>
</tr>
<tr>
<th><tt>reset(A*)</tt><br/></th>
<td align="center">C</td>
<td align="center">T</td>
<td align="center">C</td>
<td align="center">F</td>
</tr>
</table>
</blockquote>
<p>
Any operations requiring pointer conversions require complete types for both
<tt>unique_ptr</tt> and <tt>shared_ptr</tt>.
</p>
</body>
</html>