|
6 | 6 | \begin{block}{The main idea}
|
7 | 7 | \begin{itemize}
|
8 | 8 | \item substitution replaces template parameters with the provided arguments (types or values)
|
9 |
| - \item if it leads to invalid code, do not fail but try other overloads |
| 9 | + \item if it leads to invalid code, do not report an error but try other overloads/specializations |
10 | 10 | \end{itemize}
|
11 | 11 | \end{block}
|
12 | 12 | \begin{exampleblock}{Example}
|
13 | 13 | \begin{cppcode*}{}
|
14 | 14 | template <typename T>
|
15 | 15 | void f(typename T::type arg) { ... }
|
16 |
| - |
17 | 16 | void f(int a) { ... }
|
18 | 17 |
|
19 | 18 | f(1); // Calls void f(int)
|
20 | 19 | \end{cppcode*}
|
21 | 20 | \end{exampleblock}
|
22 |
| - Note : SFINAE is largely superseded by concepts in \cpp20 |
| 21 | + \begin{alertblock}{} |
| 22 | + Note: SFINAE is largely superseded by concepts in \cpp20 |
| 23 | + \end{alertblock} |
23 | 24 | \end{frame}
|
24 | 25 |
|
25 | 26 | \begin{frame}[fragile]
|
26 |
| - \frametitlecpp[11]{decltype} |
| 27 | + \frametitlecpp[11]{\texttt{decltype}} |
27 | 28 | \begin{block}{The main idea}
|
28 | 29 | \begin{itemize}
|
29 | 30 | \item gives the type of the result of an expression
|
30 |
| - \item the expression is not evaluated |
| 31 | + \item the expression is not evaluated (i.e.\ unevaluated context) |
31 | 32 | \item at compile time
|
32 | 33 | \end{itemize}
|
33 | 34 | \end{block}
|
34 | 35 | \begin{exampleblock}{Example}
|
35 | 36 | \begin{cppcode*}{}
|
36 | 37 | struct A { double x; };
|
37 | 38 | A a;
|
38 |
| - decltype(a.x) y; // double |
39 |
| - decltype((a.x)) z = y; // double& (lvalue) |
| 39 | + decltype(a.x) y; // double |
| 40 | + decltype((a.x)) z = y; // double& (lvalue) |
40 | 41 | decltype(1 + 2u) i = 4; // unsigned int
|
41 | 42 |
|
42 | 43 | template<typename T, typename U>
|
|
47 | 48 | \end{frame}
|
48 | 49 |
|
49 | 50 | \begin{frame}[fragile]
|
50 |
| - \frametitlecpp[11]{declval} |
| 51 | + \frametitlecpp[11]{\texttt{declval}} |
51 | 52 | \begin{block}{The main idea}
|
52 | 53 | \begin{itemize}
|
53 | 54 | \item gives you a reference to a ``fake'' object at compile time
|
|
58 | 59 | \begin{exampleblock}{}
|
59 | 60 | \begin{cppcode*}{}
|
60 | 61 | struct Default {
|
61 |
| - int foo() const { return 1; } |
| 62 | + int foo() const; |
62 | 63 | };
|
63 |
| - struct NonDefault { |
64 |
| - NonDefault(int i) { } |
65 |
| - int foo() const { return 1; } |
| 64 | + class NonDefault { |
| 65 | + private: NonDefault(); |
| 66 | + public: int foo() const; |
66 | 67 | };
|
67 | 68 | decltype(Default().foo()) n1 = 1; // int
|
68 |
| - decltype(NonDefault().foo()) n2 = n1; // error |
69 |
| - decltype(std::declval<NonDefault>().foo()) n2 = n1; |
| 69 | + decltype(NonDefault().foo()) n2 = 2; // error |
| 70 | + decltype(std::declval<NonDefault>().foo()) n3 = 3; |
70 | 71 | \end{cppcode*}
|
71 | 72 | \end{exampleblock}
|
72 | 73 | \end{frame}
|
73 | 74 |
|
74 | 75 | \begin{frame}[fragile]
|
75 |
| - \frametitlecpp[11]{true\_type and false\_type} |
| 76 | + \frametitlecpp[11]{\texttt{true\_type} and \texttt{false\_type}} |
76 | 77 | \begin{block}{The main idea}
|
77 | 78 | \begin{itemize}
|
78 | 79 | \item encapsulate a compile-time boolean as type
|
|
89 | 90 | constexpr bool test = t; // true
|
90 | 91 | \end{cppcode*}
|
91 | 92 | \end{exampleblock}
|
| 93 | + \begin{exampleblock}{Possible implementation} |
| 94 | + \begin{cppcode*}{} |
| 95 | + using true_type = integral_constant<bool, true >; |
| 96 | + using false_type = integral_constant<bool, false>; |
| 97 | + \end{cppcode*} |
| 98 | + \end{exampleblock} |
92 | 99 | \end{frame}
|
93 | 100 |
|
94 | 101 | \begin{frame}[fragile]
|
95 | 102 | \frametitlecpp[11]{Using SFINAE for introspection}
|
96 | 103 | \begin{block}{The main idea}
|
97 | 104 | \begin{itemize}
|
98 |
| - \item use a template specialization |
99 |
| - \begin{itemize} |
100 |
| - \item that may or may not create valid code |
101 |
| - \end{itemize} |
102 |
| - \item use SFINAE to choose between them |
103 |
| - \item inherit from true/false\_type |
| 105 | + \item use a primary template, inheriting from \mintinline{cpp}{false_type} |
| 106 | + \item use a template specialization, inheriting from \mintinline{cpp}{true_type} |
| 107 | + \begin{itemize} |
| 108 | + \item which depends on the feature you want to introspect, |
| 109 | + as part of the context where template argument deduction happens |
| 110 | + \end{itemize} |
| 111 | + \item let SFINAE choose between the two templates |
104 | 112 | \end{itemize}
|
105 | 113 | \end{block}
|
106 | 114 | \begin{exampleblock}{Example}
|
107 | 115 | \small
|
108 | 116 | \begin{cppcode*}{}
|
109 | 117 | template <typename T, typename = void>
|
110 |
| - struct hasFoo : std::false_type {}; |
| 118 | + struct hasFoo : std::false_type {}; // primary template |
111 | 119 | template <typename T>
|
112 | 120 | struct hasFoo<T, decltype(std::declval<T>().foo())>
|
113 |
| - : std::true_type {}; |
| 121 | + : std::true_type {}; // template special. |
114 | 122 | struct A{}; struct B{ void foo(); };
|
115 | 123 | static_assert(!hasFoo<A>::value, "A has no foo()");
|
116 | 124 | static_assert(hasFoo<B>::value, "B has foo()");
|
|
130 | 138 | : std::true_type {};
|
131 | 139 |
|
132 | 140 | struct A{};
|
133 |
| - struct B{void foo();}; |
134 |
| - struct C{int foo();}; |
| 141 | + struct B{ void foo(); }; |
| 142 | + struct C{ int foo(); }; |
135 | 143 |
|
136 | 144 | static_assert(!hasFoo<A>::value, "A has no foo()");
|
137 | 145 | static_assert(hasFoo<B>::value, "B has foo()");
|
138 |
| - static_assert(!hasFoo<C>::value, "C has foo()"); |
| 146 | + static_assert(!hasFoo<C>::value, "C has no foo()"); |
139 | 147 | static_assert(hasFoo<C,int>::value, "C has foo()");
|
140 | 148 | \end{cppcode*}
|
141 | 149 | \end{exampleblock}
|
|
146 | 154 | \begin{block}{Concept}
|
147 | 155 | \begin{itemize}
|
148 | 156 | \item Maps a sequence of given types to \mintinline{cpp}{void}
|
149 |
| - \item Introduced in \cpp17 though trivial to implement in \cpp11 |
| 157 | + \item Introduced in \cpp17, though trivial to implement in \cpp11 |
150 | 158 | \item Can be used in specializations to check the validity of an expression
|
151 | 159 | \end{itemize}
|
152 | 160 | \end{block}
|
|
159 | 167 | \end{frame}
|
160 | 168 |
|
161 | 169 | \begin{frame}[fragile]
|
162 |
| - \frametitlecpp[17]{Previous example using \texttt{void\_t}} |
| 170 | + \frametitlecpp[17]{Previous introspection example using \texttt{void\_t}} |
163 | 171 | \begin{exampleblock}{Example}
|
164 | 172 | \begin{cppcode*}{}
|
165 | 173 | template <typename T, typename = void>
|
|
181 | 189 | \end{frame}
|
182 | 190 |
|
183 | 191 | \begin{frame}[fragile]
|
184 |
| - \frametitle{SFINAE and the STL \hfill \cpp11/\cpp14/\cpp17} |
185 |
| - \begin{block}{enable\_if / enable\_if\_t} |
186 |
| - \begin{cppcode*}{gobble=2} |
187 |
| - template<bool B, typename T=void> struct enable_if {}; |
188 |
| - template<typename T> |
189 |
| - struct enable_if<true, T> { using type = T; }; |
190 |
| - template<bool B, typename T = void> |
191 |
| - using enable_if_t = typename enable_if<B,T>::type; |
192 |
| - \end{cppcode*} |
| 192 | + \frametitlecpp[11]{Standard type traits} |
| 193 | + \begin{block}{Standard type traits} |
193 | 194 | \begin{itemize}
|
194 |
| - \item If \mintinline{cpp}{B} is true, has an alias \mintinline{cpp}{type} to type \mintinline{cpp}{T} |
195 |
| - \item otherwise, has no \mintinline{cpp}{type} alias |
| 195 | + \item Don't implement SFINAE introspection for everything,\\ |
| 196 | + use the standard library! |
| 197 | + \item Standard type traits in header \mintinline{cpp}{<type_traits>} |
| 198 | + \item They look like \mintinline{cpp}{std::is_*<T>} |
| 199 | + \item Checks at compile time whether \mintinline{cpp}{T} is |
| 200 | + \begin{itemize} |
| 201 | + \item float, signed, final, abstract, default\_constructible, ... |
| 202 | + \end{itemize} |
| 203 | + \item Result in nested boolean constant \mintinline{cpp}{value} |
| 204 | + \item Since \cpp17, variable template versions: \mintinline{cpp}{std::is_*_v<T>}, giving the bool directly |
196 | 205 | \end{itemize}
|
197 | 206 | \end{block}
|
198 |
| - \begin{block}{is\_*$<T>$/is\_*\_v$<T>$ (float/signed/object/final/abstract/...)} |
| 207 | + \begin{cppcode*}{} |
| 208 | + constexpr bool b = std::is_pointer<int*>::value; |
| 209 | + constexpr bool b = std::is_pointer_v<int*>; // C++17 |
| 210 | + \end{cppcode*} |
| 211 | +\end{frame} |
| 212 | + |
| 213 | +\begin{frame}[fragile] |
| 214 | + \frametitle{SFINAE and the STL \hfill \cpp11/\cpp14} |
| 215 | + \begin{block}{\texttt{enable\_if}} |
| 216 | + \begin{cppcode*}{linenos=false} |
| 217 | + template<bool B, typename T=void> |
| 218 | + struct enable_if; |
| 219 | + \end{cppcode*} |
199 | 220 | \begin{itemize}
|
200 |
| - \item Standard type traits in header \mintinline{cpp}{<type_traits>} |
201 |
| - \item Checks at compile time whether \mintinline{cpp}{T} is ... |
202 |
| - \item Result in boolean member \mintinline{cpp}{value} |
| 221 | + \item If \mintinline{cpp}{B} is true, has a nested alias \mintinline{cpp}{type} to type \mintinline{cpp}{T} |
| 222 | + \item otherwise, has no such alias |
203 | 223 | \end{itemize}
|
204 | 224 | \end{block}
|
| 225 | + \begin{exampleblock}{Example} |
| 226 | + \begin{cppcode*}{} |
| 227 | + template<bool B, typename T=void> |
| 228 | + struct enable_if {}; |
| 229 | + template<typename T> |
| 230 | + struct enable_if<true, T> { using type = T; }; |
| 231 | + |
| 232 | + template<bool B, typename T = void> // C++14 |
| 233 | + using enable_if_t = typename enable_if<B, T>::type; |
| 234 | + \end{cppcode*} |
| 235 | + \end{exampleblock} |
205 | 236 | \end{frame}
|
206 | 237 |
|
207 | 238 | \begin{frame}[fragile]
|
|
218 | 249 | In& operator()(In* in) const {
|
219 | 250 | assert(in!=nullptr); return *in;
|
220 | 251 | }
|
221 |
| - } deref{}; |
| 252 | + } deref; |
222 | 253 | \end{cppcode*}
|
223 | 254 | \end{exampleblock}
|
224 | 255 | \end{frame}
|
225 | 256 |
|
226 | 257 | \begin{frame}[fragile]
|
227 |
| - \frametitlecpp[11]{Back to variadic templated class} |
228 |
| - \begin{block}{The tuple get method} |
| 258 | + \frametitlecpp[11]{Back to variadic class templates} |
| 259 | + \begin{block}{The \texttt{tuple\_element} trait} |
229 | 260 | \begin{cppcode*}{}
|
230 | 261 | template <size_t I, typename Tuple>
|
231 |
| - struct elem_type; |
| 262 | + struct tuple_element; |
232 | 263 |
|
233 | 264 | template <typename T, typename... Ts>
|
234 |
| - struct elem_type<0, tuple<T, Ts...>> { |
| 265 | + struct tuple_element<0, tuple<T, Ts...>> { |
235 | 266 | using type = T;
|
236 | 267 | };
|
237 | 268 |
|
238 | 269 | template <size_t I, typename T, typename... Ts>
|
239 |
| - struct elem_type<I, tuple<T, Ts...>> { |
240 |
| - using type = typename elem_type |
241 |
| - <I - 1, tuple<Ts...>>::type; |
| 270 | + struct tuple_element<I, tuple<T, Ts...>> { |
| 271 | + using type = typename |
| 272 | + tuple_element<I - 1, tuple<Ts...>>::type; |
242 | 273 | };
|
243 | 274 | \end{cppcode*}
|
244 | 275 | \end{block}
|
245 | 276 | \end{frame}
|
246 | 277 |
|
247 | 278 | \begin{frame}[fragile]
|
248 |
| - \frametitlecpp[14]{Back to variadic templated class} |
| 279 | + \frametitlecpp[14]{Back to variadic class templates} |
249 | 280 | \begin{block}{The tuple get function}
|
250 |
| - \begin{cppcode*}{} |
| 281 | + \begin{cppcode*}{gobble=2} |
251 | 282 | template <size_t I, typename... Ts>
|
252 | 283 | std::enable_if_t<I == 0,
|
253 |
| - typename elem_type<0, tuple<Ts...>>::type&> |
| 284 | + typename tuple_element<0, tuple<Ts...>>::type&> |
254 | 285 | get(tuple<Ts...>& t) {
|
255 | 286 | return t.m_head;
|
256 | 287 | }
|
257 | 288 | template <size_t I, typename T, typename... Ts>
|
258 | 289 | std::enable_if_t<I != 0,
|
259 |
| - typename elem_type<I - 1, tuple<Ts...>>::type&> |
| 290 | + typename tuple_element<I - 1, tuple<Ts...>>::type&> |
260 | 291 | get(tuple<T, Ts...>& t) {
|
261 |
| - tuple<Ts...>& base = t; |
262 |
| - return get<I - 1>(base); |
| 292 | + return get<I - 1>(static_cast<tuple<Ts...>&>(t)); |
263 | 293 | }
|
264 | 294 | \end{cppcode*}
|
265 | 295 | \end{block}
|
266 | 296 | \end{frame}
|
267 | 297 |
|
268 | 298 | \begin{frame}[fragile]
|
269 |
| - \frametitlecpp[17]{with if constexpr} |
| 299 | + \frametitlecpp[17]{with if constexpr and return type deduction} |
270 | 300 | \begin{block}{The tuple get function}
|
271 | 301 | \begin{cppcode*}{gobble=2}
|
272 | 302 | template <size_t I, typename T, typename... Ts>
|
|
0 commit comments