In this section, we define some simple toy function objects and concatenate them, so we get a single function that applies the simple toy functions after each other to the input we give it. In order to do so, we write our own concatenation helper function:
- First, we need some includes:
#include <iostream>
#include <functional>
- Then, we implement the helper function, concat, which arbitrarily takes many parameters. These parameters will be functions, such as f, g, and h, and the result will be another function object that applies f(g(h(...))) on any input:
template <typename T, typename ...Ts>
auto concat(T t, Ts ...ts)
{
- Now, it gets a little complicated. When the user provides functions f, g, and h, we will evaluate this to f( concat(g, h) ), which again expands to f( g( concat(h) ) ), where the recursion aborts, so we get f( g( h(...) ) ). This chain of function calls representing the concatenation of these user functions is captured by a lambda expression, which can later take some parameters, p, and then forward them to f(g(h(p))). This lambda expression is what we return. The if constexpr construct checks whether we are in a recursion step with more than one function to concatenate left:
if constexpr (sizeof...(ts) > 0) {
return [=](auto ...parameters) {
return t(concat(ts...)(parameters...));
};
}
- The other branch of the if constexpr construct is selected by the compiler if we are at the end of the recursion. In such a case, we just return the function, t, because it is the only parameter left:
else {
return t;
}
}
- Now, let's use our cool new function concatenation helper with some functions we want to see concatenated. Let's begin with the main function, where we define two cheap simple function objects:
int main()
{
auto twice ([] (int i) { return i * 2; });
auto thrice ([] (int i) { return i * 3; });
- Now let's concatenate. We concatenate our two multiplier function objects with the STL function, std::plus<int>, which takes two parameters and simply returns their sum. This way, we get a function that does twice(thrice(plus( a, b ))).
auto combined (
concat(twice, thrice, std::plus<int>{})
);
- Now let's use it. The combined function looks like a single normal function now, and the compiler is also able to concatenate those functions without any unnecessary overhead:
std::cout << combined(2, 3) << 'n';
}
- Compiling and running our program yields the following output, which we also expected, because 2 * 3 * (2 + 3) is 30:
$ ./concatenation
30