|
3 | 3 | %http://eli.thegreenplace.net/2014/perfect-forwarding-and-universal-references-in-c/ |
4 | 4 | \begin{frame}[fragile] |
5 | 5 | \frametitlecpp[11]{The problem} |
6 | | - Trying to write a generic wrapper function |
7 | | - \begin{cppcode*}{} |
8 | | - template <typename T> |
9 | | - void wrapper(T arg) { |
10 | | - func(arg); |
11 | | - } |
12 | | - \end{cppcode*} |
| 6 | + How to write a generic wrapper function? |
| 7 | + \begin{block}{} |
| 8 | + \begin{cppcode*}{} |
| 9 | + template <typename T> |
| 10 | + void wrapper(T arg) { |
| 11 | + // code before |
| 12 | + func(arg); |
| 13 | + // code after |
| 14 | + } |
| 15 | + \end{cppcode*} |
| 16 | + \end{block} |
13 | 17 | Example usage : |
14 | 18 | \begin{itemize} |
15 | | - \item emplace\_back |
16 | | - \item make\_unique |
| 19 | + \item \mintinline{cpp}{emplace_back} |
| 20 | + \item \mintinline{cpp}{make_unique} |
17 | 21 | \end{itemize} |
18 | 22 | \end{frame} |
19 | 23 |
|
20 | 24 | \begin{frame}[fragile] |
21 | 25 | \frametitlecpp[11]{Why is it not so simple?} |
22 | | - \begin{cppcode*}{} |
23 | | - template <typename T> |
24 | | - void wrapper(T arg) { |
25 | | - func(arg); |
26 | | - } |
27 | | - \end{cppcode*} |
| 26 | + \begin{block}{} |
| 27 | + \begin{cppcode*}{} |
| 28 | + template <typename T> |
| 29 | + void wrapper(T arg) { |
| 30 | + func(arg); |
| 31 | + } |
| 32 | + \end{cppcode*} |
| 33 | + \end{block} |
28 | 34 | \begin{alertblock}{What about references ?} |
29 | 35 | what if func takes references to avoid copies ?\\ |
30 | 36 | wrapper would force a copy and we fail to use references |
31 | 37 | \end{alertblock} |
32 | 38 | \end{frame} |
33 | 39 |
|
34 | 40 | \begin{frame}[fragile] |
35 | | - \frametitlecpp[11]{Second try, second failure?} |
36 | | - \begin{cppcode*}{} |
37 | | - template <typename T> |
38 | | - void wrapper(T &arg) { |
39 | | - func(arg); |
40 | | - } |
41 | | - wrapper(42); |
42 | | - // invalid initialization of |
43 | | - // non-const reference from |
44 | | - // an rvalue |
45 | | - \end{cppcode*} |
| 41 | + \frametitlecpp[11]{Second try, second failure ?} |
| 42 | + \begin{block}{} |
| 43 | + \begin{cppcode*}{} |
| 44 | + template <typename T> |
| 45 | + void wrapper(T& arg) { |
| 46 | + func(arg); |
| 47 | + } |
| 48 | + wrapper(42); |
| 49 | + // invalid initialization of |
| 50 | + // non-const reference from |
| 51 | + // an rvalue |
| 52 | + \end{cppcode*} |
| 53 | + \end{block} |
46 | 54 | \begin{alertblock}{} |
47 | | - const ref won't help : you may want to pass something non const\\ |
48 | | - and rvalue are not yet supported... |
| 55 | + \begin{itemize} |
| 56 | + \item \mintinline{cpp}{const T&} won't work when passing something non const |
| 57 | + \item rvalues are not supported in either case |
| 58 | + \end{itemize} |
49 | 59 | \end{alertblock} |
50 | 60 | \end{frame} |
51 | 61 |
|
52 | 62 | \begin{frame}[fragile] |
53 | 63 | \frametitlecpp[11]{The solution: cover all cases} |
54 | | - \begin{cppcode*}{} |
55 | | - template <typename T> |
56 | | - void wrapper(T& arg) { func(arg); } |
| 64 | + \begin{block}{} |
| 65 | + \begin{cppcode*}{} |
| 66 | + template <typename T> |
| 67 | + void wrapper(T& arg) { func(arg); } |
57 | 68 |
|
58 | | - template <typename T> |
59 | | - void wrapper(const T& arg) { func(arg); } |
| 69 | + template <typename T> |
| 70 | + void wrapper(const T& arg) { func(arg); } |
60 | 71 |
|
61 | | - template <typename T> |
62 | | - void wrapper(T&& arg) { func(arg); } |
63 | | - \end{cppcode*} |
| 72 | + template <typename T> |
| 73 | + void wrapper(T&& arg) { func(arg); } |
| 74 | + \end{cppcode*} |
| 75 | + \end{block}{} |
64 | 76 | \end{frame} |
65 | 77 |
|
66 | 78 | \begin{frame}[fragile] |
67 | 79 | \frametitlecpp[11]{The new problem: scaling to n arguments} |
68 | | - \begin{cppcode*}{} |
69 | | - template <typename T1, typename T2> |
70 | | - void wrapper(T1& arg1, T2& arg2) |
71 | | - { func(arg1, arg2); } |
72 | | - |
73 | | - template <typename T1, typename T2> |
74 | | - void wrapper(const T1& arg1, T2& arg2) |
75 | | - { func(arg1, arg2); } |
76 | | - |
77 | | - template <typename T1, typename T2> |
78 | | - void wrapper(T1& arg1, const T2& arg2) |
79 | | - { func(arg1, arg2); } |
80 | | - ... |
81 | | - \end{cppcode*} |
| 80 | + \begin{block}{} |
| 81 | + \begin{cppcode*}{} |
| 82 | + template <typename T1, typename T2> |
| 83 | + void wrapper(T1& arg1, T2& arg2) |
| 84 | + { func(arg1, arg2); } |
| 85 | + |
| 86 | + template <typename T1, typename T2> |
| 87 | + void wrapper(const T1& arg1, T2& arg2) |
| 88 | + { func(arg1, arg2); } |
| 89 | + |
| 90 | + template <typename T1, typename T2> |
| 91 | + void wrapper(T1& arg1, const T2& arg2) |
| 92 | + { func(arg1, arg2); } |
| 93 | + ... |
| 94 | + \end{cppcode*} |
| 95 | + \end{block}{} |
82 | 96 | \begin{alertblock}{Exploding complexity} |
83 | 97 | 3$^{n}$ complexity\\ |
84 | 98 | you do not want to try n = 5... |
85 | 99 | \end{alertblock} |
86 | 100 | \end{frame} |
87 | 101 |
|
88 | 102 | \begin{frame}[fragile] |
89 | | - \frametitlecpp[11]{Reference collapsing in \cpp98} |
| 103 | + \frametitlecpp[11]{Reference collapsing} |
90 | 104 | \begin{block}{Reference to references} |
91 | | - They are forbidden in \cpp\\ |
92 | | - But still may happen |
| 105 | + \begin{itemize} |
| 106 | + \item They are forbidden, but some compilers allow them |
| 107 | + \end{itemize} |
| 108 | + \end{block} |
| 109 | + \begin{block}{} |
93 | 110 | \begin{cppcode*}{} |
94 | 111 | template <typename T> |
95 | | - void foo(T t) { |
96 | | - T& k = t; |
97 | | - ... |
98 | | - } |
| 112 | + void foo(T t) { T& k = t; } // int& & |
99 | 113 | int ii = 4; |
100 | 114 | foo<int&>(ii); |
101 | 115 | \end{cppcode*} |
102 | 116 | \end{block} |
103 | | - \begin{exampleblock}{Practically} |
104 | | - all compilers were collapsing the 2 references |
105 | | - \end{exampleblock} |
106 | | -\end{frame} |
107 | | - |
108 | | -\begin{frame} |
109 | | - \frametitlecpp[11]{Reference collapsing in \cpp11} |
110 | | - \begin{block}{rvalues have been added} |
| 117 | + \begin{block}{\cpp11 added rvalues} |
111 | 118 | \begin{itemize} |
112 | | - \item what about int\&\&\&? |
113 | | - \item and int \&\&\&\&? |
| 119 | + \item what about \mintinline{cpp}{int&& &} ? |
| 120 | + \item and int \mintinline{cpp}{&& &&} ? |
114 | 121 | \end{itemize} |
115 | 122 | \end{block} |
116 | | - \begin{exampleblock}{\cpp11 standardization} |
117 | | - The rule is simple : \& always wins\\ |
118 | | - \&\&\&, \&\&\&, \&\& $\rightarrow$ \&\\ |
119 | | - \&\&\&\& $\rightarrow$ \&\& |
| 123 | + \begin{exampleblock}{Rule} |
| 124 | + \mintinline{cpp}{&} always wins\\ |
| 125 | + \mintinline{cpp}{&& &, & &&, & &} $\rightarrow$ \mintinline{cpp}{&}\\ |
| 126 | + \mintinline{cpp}{&& &&} $\rightarrow$ \mintinline{cpp}{&&} |
120 | 127 | \end{exampleblock} |
121 | 128 | \end{frame} |
122 | 129 |
|
123 | 130 | \begin{frame}[fragile] |
124 | 131 | \frametitlecpp[11]{rvalue in type-deducing context} |
125 | | - \begin{cppcode*}{} |
126 | | - template <typename T> |
127 | | - void func(T&& t) {} |
128 | | - \end{cppcode*} |
129 | | - Next to a template parameter, \&\& is not an rvalue, but a ``universal reference''\\ |
130 | | - T\&\& actual type depends on the arguments passed to func |
| 132 | + \begin{block}{} |
| 133 | + \begin{cppcode*}{} |
| 134 | + template <typename T> |
| 135 | + void func(T&& t) {} |
| 136 | + \end{cppcode*} |
| 137 | + \end{block} |
| 138 | + Next to a template parameter, \mintinline{cpp}{&&} is not an rvalue, but a ``forwarding reference'' (aka. ``universal reference'')\\ |
| 139 | + \mintinline{cpp}{T&&} actual type depends on the arguments passed to func |
131 | 140 | \begin{itemize} |
132 | | - \item if an lvalue of type U is given, T is deduced to U\& |
| 141 | + \item if an lvalue of type U is given, T is deduced to \mintinline{cpp}{U&} |
133 | 142 | \item otherwise, collapse references normally |
134 | 143 | \end{itemize} |
135 | | - \begin{cppcode*}{firstnumber=3} |
136 | | - func(4); // rvalue -> T&& is int&& |
137 | | - double d = 3.14; |
138 | | - func(d); // lvalue -> T&& is double& |
139 | | - float f() {...} |
140 | | - func(f()); // rvalue -> T&& is float&& |
141 | | - int foo(int i) { |
142 | | - func(i); // lvalue -> T&& is int& |
143 | | - } |
144 | | - \end{cppcode*} |
| 144 | + \begin{block}{} |
| 145 | + \begin{cppcode*}{firstnumber=3} |
| 146 | + func(4); // rvalue -> T&& is int&& |
| 147 | + double d = 3.14; |
| 148 | + func(d); // lvalue -> T&& is double& |
| 149 | + float f() {...} |
| 150 | + func(f()); // rvalue -> T&& is float&& |
| 151 | + int foo(int i) { |
| 152 | + func(i); // lvalue -> T&& is int& |
| 153 | + } |
| 154 | + \end{cppcode*} |
| 155 | + \end{block} |
145 | 156 | \end{frame} |
146 | 157 |
|
147 | 158 | \begin{frame}[fragile] |
148 | 159 | \frametitlecpp[11]{std::remove\_reference} |
149 | | - Some template trickery removing reference from a type |
150 | | - \begin{cppcode*}{} |
151 | | - template< typename T > |
152 | | - struct remove_reference |
153 | | - {using type = T;}; |
154 | | - |
155 | | - template< typename T > |
156 | | - struct remove_reference<T&> |
157 | | - {using type = T;}; |
158 | | - |
159 | | - template< typename T > |
160 | | - struct remove_reference<T&&> |
161 | | - {using type = T;}; |
162 | | - \end{cppcode*} |
163 | | - If {\ttfamily T} is a reference type, {\ttfamily remove\_reference<T>::type} is the type referred to by T, |
164 | | - otherwise it is T. |
| 160 | + Type trait to remove reference from a type |
| 161 | + \begin{block}{} |
| 162 | + \begin{cppcode*}{} |
| 163 | + template <typename T> |
| 164 | + struct remove_reference { using type = T; }; |
| 165 | + |
| 166 | + template <typename T> |
| 167 | + struct remove_reference<T&> { using type = T; }; |
| 168 | + |
| 169 | + template <typename T> |
| 170 | + struct remove_reference<T&&> { using type = T; }; |
| 171 | + \end{cppcode*} |
| 172 | + \end{block} |
| 173 | + If {\ttfamily T} is a reference type, \mintinline{cpp}{remove_reference_t<T>::type} is the type referred to by {\ttfamily T}, |
| 174 | + otherwise it is {\ttfamily T}. |
165 | 175 | \end{frame} |
166 | 176 |
|
167 | 177 | \begin{frame}[fragile] |
168 | 178 | \frametitlecpp[11]{std::forward} |
169 | | - Another template trickery keeping references and mapping non reference types to rvalue references |
170 | | - \begin{cppcode*}{} |
171 | | - template<typename T> |
172 | | - T&& forward(typename std::remove_reference<T>::type& t) |
173 | | - noexcept { |
174 | | - return static_cast<T&&>(t); |
175 | | - } |
176 | | - \end{cppcode*} |
| 179 | + Keeps references and maps non-reference types to rvalue references |
| 180 | + \begin{block}{} |
| 181 | + \small |
| 182 | + \begin{cppcode*}{} |
| 183 | + template<typename T> |
| 184 | + T&& forward(typename std::remove_reference<T> |
| 185 | + ::type& t) noexcept { |
| 186 | + return static_cast<T&&>(t); |
| 187 | + } |
| 188 | + template<typename T> |
| 189 | + T&& forward(typename std::remove_reference<T> |
| 190 | + ::type&& t) noexcept { |
| 191 | + return static_cast<T&&>(t); |
| 192 | + } |
| 193 | + \end{cppcode*} |
| 194 | + \end{block} |
177 | 195 | \begin{block}{How it works} |
178 | 196 | \begin{itemize} |
179 | | - \item if T is int, it returns int \&\& |
180 | | - \item if T is int\&, it returns int\&\&\& ie int\& |
181 | | - \item if T is int\&\&, it returns int\&\&\&\& ie int\&\& |
| 197 | + \item if T is \mintinline{cpp}{int}, it returns \mintinline{cpp}{int&&} |
| 198 | + \item if T is \mintinline{cpp}{int&}, it returns \mintinline{cpp}{int& &&} ie. \mintinline{cpp}{int&} |
| 199 | + \item if T is \mintinline{cpp}{int&&}, it returns \mintinline{cpp}{int&& &&} ie. \mintinline{cpp}{int&&} |
182 | 200 | \end{itemize} |
183 | 201 | \end{block} |
184 | 202 | \end{frame} |
185 | 203 |
|
186 | 204 | \begin{frame}[fragile] |
187 | 205 | \frametitlecpp[11]{Perfect forwarding} |
188 | 206 | Putting it all together |
189 | | - \begin{cppcode*}{} |
190 | | - template <typename... T> |
191 | | - void wrapper(T&&... args) { |
192 | | - func(std::forward<T>(args)...); |
193 | | - } |
194 | | - \end{cppcode*} |
195 | | - \begin{block}{How it works} |
| 207 | + \begin{block}{} |
| 208 | + \begin{cppcode*}{} |
| 209 | + template <typename... T> |
| 210 | + void wrapper(T&&... args) { |
| 211 | + func(std::forward<T>(args)...); |
| 212 | + } |
| 213 | + \end{cppcode*} |
| 214 | + \end{block} |
| 215 | + \begin{block}{} |
196 | 216 | \begin{itemize} |
197 | | - \item if we pass an rvalue to wrapper (U\&\&) |
| 217 | + \item if we pass an rvalue to wrapper (\mintinline{cpp}{U&&}) |
198 | 218 | \begin{itemize} |
199 | | - \item arg will be of type U\&\& |
200 | | - \item func will be called with a U\&\& |
| 219 | + \item arg will be of type \mintinline{cpp}{U&&} |
| 220 | + \item func will be called with a \mintinline{cpp}{U&&} |
201 | 221 | \end{itemize} |
202 | | - \item if we pass an lvalue to wrapper (U\&) |
| 222 | + \item if we pass an lvalue to wrapper (\mintinline{cpp}{U&}) |
203 | 223 | \begin{itemize} |
204 | | - \item arg will be of type U\& |
205 | | - \item func will be called with a U\& |
| 224 | + \item arg will be of type \mintinline{cpp}{U&} |
| 225 | + \item func will be called with a \mintinline{cpp}{U&} |
206 | 226 | \end{itemize} |
207 | | - \item if we pass a plain value (U) |
| 227 | + \item if we pass a plain value (\mintinline{cpp}{U}) |
208 | 228 | \begin{itemize} |
209 | | - \item arg will be of type U\&\& (no copy in wrapper) |
210 | | - \item func will be called with a U\&\& |
211 | | - \item but func takes a U, so copy happens there, as expected |
| 229 | + \item arg will be of type \mintinline{cpp}{U&&} (no copy in wrapper) |
| 230 | + \item func will be called with a \mintinline{cpp}{U&&} |
| 231 | + \item but func takes a \mintinline{cpp}{U&}, so copy happens there, as expected |
212 | 232 | \end{itemize} |
213 | 233 | \end{itemize} |
214 | 234 | \end{block} |
215 | 235 | \end{frame} |
216 | 236 |
|
217 | 237 | \begin{frame}[fragile] |
218 | 238 | \frametitlecpp[11]{Real life example} |
219 | | - \begin{cppcode*}{} |
220 | | - template<typename T, typename... Args> |
221 | | - unique_ptr<T> make_unique(Args&&... args) { |
222 | | - return unique_ptr<T> |
223 | | - (new T(std::forward<Args>(args)...)); |
224 | | - } |
225 | | - \end{cppcode*} |
| 239 | + \begin{block}{} |
| 240 | + \begin{cppcode*}{} |
| 241 | + template<typename T, typename... Args> |
| 242 | + unique_ptr<T> make_unique(Args&&... args) { |
| 243 | + return unique_ptr<T> |
| 244 | + (new T(std::forward<Args>(args)...)); |
| 245 | + } |
| 246 | + \end{cppcode*} |
| 247 | + \end{block} |
226 | 248 | \end{frame} |
0 commit comments