|
30 | 30 |
|
31 | 31 | \begin{document} |
32 | 32 |
|
33 | | -\section{Recursion} |
34 | | -Implement a lambda expression \texttt{f(int x)} that calculates $x!$ (factorial of \texttt{x}) by recursion upon calling! |
35 | | -Use the following skeleton: |
36 | | -\begin{lstlisting} |
37 | | -int main() { |
38 | | - auto f = /* TODO */ |
39 | | - return x < 2 ? x : x * f( /* TODO */ |
40 | | - }); |
41 | | - |
42 | | - return f(3); |
43 | | -} |
44 | | -\end{lstlisting} |
45 | | - |
46 | | -\section{Function traits} |
47 | | -\label{sec:fcttraits} |
48 | | -Finish the implementation of \texttt{function\_traits} and \texttt{lambda\_traits}, such that the program compiles successfully! Note, that it is sufficient for \texttt{function\_traits} to only accept instances of \texttt{std::function}. Think about how you can use your implementation of \texttt{function\_traits} with lambda expressions and implement this logic in \texttt{lambda\_traits}. Note further, that invocations of \texttt{std::function} are not critical in terms of possible performance penalties in this scenario, as long as the traits are available at compile time (as required in \texttt{main}). |
49 | | - |
50 | | -\inputcpplisting{snippet21a} |
51 | | - |
52 | | -\textbf{Hint:} You may want to use \texttt{std::tuple\_element} and \texttt{std::declval}. |
53 | | - |
54 | | -\newpage |
55 | | - |
56 | | -\section{Passing lambdas as argument} |
57 | | -Implement \texttt{Filters} as an logical \textit{or} filter gathering, such that the program below compiles and runs successfully. |
58 | | - |
59 | | -\inputcpplisting{snippet20a} |
60 | | - |
61 | | -The constructor of \texttt{Filters} should take one or more filters, |
62 | | -\begin{lstlisting} |
63 | | -template <typename F, typename... Fs> Filters(F f, Fs... fs) |
64 | | -\end{lstlisting} |
65 | | -where each filter in \texttt{fs} (and \texttt{f} itself) is a lambda (or \texttt{std::function}) that accepts an argument of type \texttt{T} and returns a boolean. \texttt{T} should be generic and is allow to be different amongst the filters. |
66 | | -The call operator |
67 | | -\begin{lstlisting} |
68 | | -bool Filters::operator()(T x) const |
69 | | -\end{lstlisting} |
70 | | -should return the logical \textit{or} of all filters for applied \texttt{x} as shown in the example. |
71 | | -Further, implement a function |
72 | | -\begin{lstlisting} |
73 | | -template <typename F> void Filters::add_filter(F) |
74 | | -\end{lstlisting} |
75 | | -that adds a filter to the gathering. Note, that in the example there is no common type in terms of \texttt{std::common\_type\_t} for \texttt{f1}, \texttt{f2} and \texttt{f3}: |
76 | | -\begin{lstlisting} |
77 | | -using T12 = std::common_type_t<decltype(f1), decltype(f2)>; // OK |
78 | | -using T123 = std::common_type_t<T12, decltype(f3)>; // Compile-time error! |
79 | | -\end{lstlisting} |
80 | | - |
81 | | -If in trouble, follow the step-by-step instructions printed below! |
82 | | - |
83 | | -\subsection{Step I} |
84 | | -Start by finding an implementation for \texttt{Filters} that suffices the relaxed requirement |
85 | | -\begin{lstlisting} |
86 | | -template <typename F1, typename F2> |
87 | | -struct Filters { |
88 | | - Filters(F1 f1, F2 f2) { |
89 | | - /* TODO */ |
90 | | - } |
91 | | - |
92 | | - [[nodiscard]] auto operator()(int x) const { |
93 | | - /* TODO */ |
94 | | - } |
95 | | -}; |
96 | | - |
97 | | -int main() { |
98 | | - auto f1 = [](int x) { return x % 2 == 0; }; |
99 | | - auto f2 = [](int x) { return x % 3 == 0; }; |
100 | | - Filters filters(f1, f2); |
101 | | - return filters(5) ? 1 : 0; |
102 | | -} |
103 | | -\end{lstlisting} |
104 | | -\ldots and answer the question why we need two separate template types \texttt{F1} and \texttt{F2}! |
105 | | - |
106 | | -\subsection{Step II} |
107 | | -Make the type of the passed argument of a filter (\texttt{int}) generic and pass it as a template parameter. You will now need to instantiate \texttt{Filters} akin to |
108 | | -\begin{lstlisting} |
109 | | -auto f1 = [](int x) { return x % 2 == 0; }; |
110 | | -auto f2 = [](int x) { return x % 3 == 0; }; |
111 | | -Filters<decltype(f1), decltype(f2), int> filters(f1, f2); |
112 | | -\end{lstlisting} |
113 | | -This is unpleasent and we will address this issue in the next step. |
114 | | - |
115 | | -\subsection{Step III} |
116 | | -Use a deduction guide to get rid of the explicit type naming of the filters. This is a delicate task, since we have to find the type of the argument of a passed filter. You may want to have a look at Sec.~\ref{sec:fcttraits} to find a solution for this problem. |
117 | | -\begin{lstlisting} |
118 | | -template <typename F1, typename F2> |
119 | | -Filters(F1 f1, F2 f2) -> Filters<F1, F2, std::common_type_t< /* TODO */ >> |
120 | | -\end{lstlisting} |
121 | | - |
122 | | -\subsection{Step IV} |
123 | | -We now generalize our solution for an arbitrary number of filters. Use variadic templates for the constructor and the deduction guide, and store the filters in a \texttt{std::vector}! Use \texttt{std::function<bool(T)>} as the value type of the vector. |
124 | | - |
125 | | -\begin{lstlisting} |
126 | | -template <typename T> |
127 | | -struct Filters { |
128 | | - std::vector<std::function<bool(T)>> filters; |
129 | | - |
130 | | - template <typename F, typename... Fs> |
131 | | - Filters(F f, Fs... fs): filters( /* TODO */ ) {} |
132 | | - |
133 | | - /* TODO */ |
134 | | -}; |
135 | | - |
136 | | -template <typename... Fs> |
137 | | -Filters(Fs... fs) |
138 | | --> Filters<std::common_type_t< /* TODO */ >>; |
139 | | -\end{lstlisting} |
140 | | -Add an sufficient implementation of |
141 | | -\begin{lstlisting} |
142 | | -template <typename F> void Filters::add_filter(F) |
143 | | -\end{lstlisting} |
144 | | -and don't forget to adopt your implementation of \texttt{Filters.operator()(T)}! (use \texttt{std::accumulate} if possible.) |
145 | | - |
146 | | -\subsection{Step V} |
147 | | -Can you think of an alternative to \texttt{std::function}? Why can we not use \texttt{std::common\_type} or \texttt{std::variant}? |
148 | | - |
149 | | -\textbf{Hint:} Compare the sizes of lambdas with different captures: |
150 | | -\begin{lstlisting}[title=\href{https://godbolt.org/z/ZBUfgY}{\texttt{godbolt.org/z/ZBUfgY}}] |
151 | | -int capture_me = 1; |
152 | | -std::cout << sizeof([](int x) { return x; }) << '\n'; |
153 | | -std::cout << sizeof([y=capture_me](int x) { return x + y; }) << '\n'; |
154 | | -std::cout << sizeof([y=&capture_me](int x) { return x + y; }) << '\n'; |
155 | | -\end{lstlisting} |
156 | | - |
157 | | -\subsection{Optional} |
158 | | -In case you are wondering what \texttt{std::common\_type} does; its rules are based on the rules for the ternary operator which can be confusing, e.g. |
159 | | -\begin{lstlisting}[title=\href{https://godbolt.org/z/4TGmK6}{\texttt{godbolt.org/z/4TGmK6}}] |
160 | | -struct S {}; |
161 | | - |
162 | | -template<class T, int> struct CT { |
163 | | - operator T() const; |
164 | | -}; |
165 | | - |
166 | | -int main() { |
167 | | - auto a = false ? CT<int, 1>{} : CT<int, 2>{}; // OK |
168 | | - auto b = false ? CT<int*, 1>{} : CT<int*, 2>{}; // OK |
169 | | - auto c = false ? CT<S*, 1>{} : CT<S*, 2>{}; // OK |
170 | | - auto d = false ? CT<S, 1>{} : CT<S, 2>{}; // Compile-time error |
171 | | -} |
172 | | -\end{lstlisting} |
173 | | -Depending on what compiler you are using, the error message for \texttt{d} varies. Take a look at: |
174 | | -\begin{itemize} |
175 | | - \item \texttt{[over.build] \S27} |
176 | | - \item \texttt{[expr.cond] \S6} |
177 | | -\end{itemize} |
178 | | -if you want to learn more. |
179 | | - |
180 | | -\newpage |
181 | | - |
182 | | -\section{Solutions} |
183 | | - |
184 | | -\subsection{Recursion} |
185 | | -\inputcpplisting{snippet35} |
186 | | - |
187 | | -\newpage |
188 | | - |
189 | | -\subsection{Function traits} |
190 | | -\inputcpplisting{snippet21} |
| 33 | +\input{tasks} |
191 | 34 |
|
192 | 35 | \newpage |
| 36 | +\input{solutions} |
193 | 37 |
|
194 | | -\subsection{Passing lambdas as argument} |
195 | | -\inputcpplisting{snippet20} |
196 | 38 | \end{document} |
0 commit comments