Skip to content

Commit a342592

Browse files
bernhardmgrubersponce
authored andcommitted
Improve SFINAE slides
1 parent 98f31a3 commit a342592

File tree

1 file changed

+88
-58
lines changed

1 file changed

+88
-58
lines changed

talk/expert/sfinae.tex

Lines changed: 88 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -6,37 +6,38 @@
66
\begin{block}{The main idea}
77
\begin{itemize}
88
\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
1010
\end{itemize}
1111
\end{block}
1212
\begin{exampleblock}{Example}
1313
\begin{cppcode*}{}
1414
template <typename T>
1515
void f(typename T::type arg) { ... }
16-
1716
void f(int a) { ... }
1817

1918
f(1); // Calls void f(int)
2019
\end{cppcode*}
2120
\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}
2324
\end{frame}
2425

2526
\begin{frame}[fragile]
26-
\frametitlecpp[11]{decltype}
27+
\frametitlecpp[11]{\texttt{decltype}}
2728
\begin{block}{The main idea}
2829
\begin{itemize}
2930
\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)
3132
\item at compile time
3233
\end{itemize}
3334
\end{block}
3435
\begin{exampleblock}{Example}
3536
\begin{cppcode*}{}
3637
struct A { double x; };
3738
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)
4041
decltype(1 + 2u) i = 4; // unsigned int
4142

4243
template<typename T, typename U>
@@ -47,7 +48,7 @@
4748
\end{frame}
4849

4950
\begin{frame}[fragile]
50-
\frametitlecpp[11]{declval}
51+
\frametitlecpp[11]{\texttt{declval}}
5152
\begin{block}{The main idea}
5253
\begin{itemize}
5354
\item gives you a reference to a ``fake'' object at compile time
@@ -58,21 +59,21 @@
5859
\begin{exampleblock}{}
5960
\begin{cppcode*}{}
6061
struct Default {
61-
int foo() const { return 1; }
62+
int foo() const;
6263
};
63-
struct NonDefault {
64-
NonDefault(int i) { }
65-
int foo() const { return 1; }
64+
class NonDefault {
65+
private: NonDefault();
66+
public: int foo() const;
6667
};
6768
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;
7071
\end{cppcode*}
7172
\end{exampleblock}
7273
\end{frame}
7374

7475
\begin{frame}[fragile]
75-
\frametitlecpp[11]{true\_type and false\_type}
76+
\frametitlecpp[11]{\texttt{true\_type} and \texttt{false\_type}}
7677
\begin{block}{The main idea}
7778
\begin{itemize}
7879
\item encapsulate a compile-time boolean as type
@@ -89,28 +90,35 @@
8990
constexpr bool test = t; // true
9091
\end{cppcode*}
9192
\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}
9299
\end{frame}
93100

94101
\begin{frame}[fragile]
95102
\frametitlecpp[11]{Using SFINAE for introspection}
96103
\begin{block}{The main idea}
97104
\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
104112
\end{itemize}
105113
\end{block}
106114
\begin{exampleblock}{Example}
107115
\small
108116
\begin{cppcode*}{}
109117
template <typename T, typename = void>
110-
struct hasFoo : std::false_type {};
118+
struct hasFoo : std::false_type {}; // primary template
111119
template <typename T>
112120
struct hasFoo<T, decltype(std::declval<T>().foo())>
113-
: std::true_type {};
121+
: std::true_type {}; // template special.
114122
struct A{}; struct B{ void foo(); };
115123
static_assert(!hasFoo<A>::value, "A has no foo()");
116124
static_assert(hasFoo<B>::value, "B has foo()");
@@ -130,12 +138,12 @@
130138
: std::true_type {};
131139

132140
struct A{};
133-
struct B{void foo();};
134-
struct C{int foo();};
141+
struct B{ void foo(); };
142+
struct C{ int foo(); };
135143

136144
static_assert(!hasFoo<A>::value, "A has no foo()");
137145
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()");
139147
static_assert(hasFoo<C,int>::value, "C has foo()");
140148
\end{cppcode*}
141149
\end{exampleblock}
@@ -146,7 +154,7 @@
146154
\begin{block}{Concept}
147155
\begin{itemize}
148156
\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
150158
\item Can be used in specializations to check the validity of an expression
151159
\end{itemize}
152160
\end{block}
@@ -159,7 +167,7 @@
159167
\end{frame}
160168

161169
\begin{frame}[fragile]
162-
\frametitlecpp[17]{Previous example using \texttt{void\_t}}
170+
\frametitlecpp[17]{Previous introspection example using \texttt{void\_t}}
163171
\begin{exampleblock}{Example}
164172
\begin{cppcode*}{}
165173
template <typename T, typename = void>
@@ -181,27 +189,50 @@
181189
\end{frame}
182190

183191
\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}
193194
\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
196205
\end{itemize}
197206
\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*}
199220
\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
203223
\end{itemize}
204224
\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}
205236
\end{frame}
206237

207238
\begin{frame}[fragile]
@@ -218,55 +249,54 @@
218249
In& operator()(In* in) const {
219250
assert(in!=nullptr); return *in;
220251
}
221-
} deref{};
252+
} deref;
222253
\end{cppcode*}
223254
\end{exampleblock}
224255
\end{frame}
225256

226257
\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}
229260
\begin{cppcode*}{}
230261
template <size_t I, typename Tuple>
231-
struct elem_type;
262+
struct tuple_element;
232263

233264
template <typename T, typename... Ts>
234-
struct elem_type<0, tuple<T, Ts...>> {
265+
struct tuple_element<0, tuple<T, Ts...>> {
235266
using type = T;
236267
};
237268

238269
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;
242273
};
243274
\end{cppcode*}
244275
\end{block}
245276
\end{frame}
246277

247278
\begin{frame}[fragile]
248-
\frametitlecpp[14]{Back to variadic templated class}
279+
\frametitlecpp[14]{Back to variadic class templates}
249280
\begin{block}{The tuple get function}
250-
\begin{cppcode*}{}
281+
\begin{cppcode*}{gobble=2}
251282
template <size_t I, typename... Ts>
252283
std::enable_if_t<I == 0,
253-
typename elem_type<0, tuple<Ts...>>::type&>
284+
typename tuple_element<0, tuple<Ts...>>::type&>
254285
get(tuple<Ts...>& t) {
255286
return t.m_head;
256287
}
257288
template <size_t I, typename T, typename... Ts>
258289
std::enable_if_t<I != 0,
259-
typename elem_type<I - 1, tuple<Ts...>>::type&>
290+
typename tuple_element<I - 1, tuple<Ts...>>::type&>
260291
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));
263293
}
264294
\end{cppcode*}
265295
\end{block}
266296
\end{frame}
267297

268298
\begin{frame}[fragile]
269-
\frametitlecpp[17]{with if constexpr}
299+
\frametitlecpp[17]{with if constexpr and return type deduction}
270300
\begin{block}{The tuple get function}
271301
\begin{cppcode*}{gobble=2}
272302
template <size_t I, typename T, typename... Ts>

0 commit comments

Comments
 (0)