Composition is usually represented by a dot (.) or asterisk (*), such as f . g or f * g. We can actually do something similar in C++ by overloading operator* (it would make little sense to try to overload operator dot). Similar to the compose() function, operator* should work with any number of arguments; therefore, we will have two overloads, just like in the case of compose():
- The first overload takes two arguments and calls compose() to return a new function.
- The second overload is a variadic template function that again calls operator* by expanding the parameter pack:
template <typename F, typename G>
auto operator*(F&& f, G&& g)
{
return compose(std::forward<F>(f), std::forward<G>(g));
}
template <typename F, typename... R>
auto operator*(F&& f, R&&... r)
{
return operator*(std::forward<F>(f), r...);
}
We can now simplify the actual composition of functions by applying operator* instead of the more verbose call to compose:
auto n =
([](int const n) {return std::to_string(n); } *
[](int const n) {return n * n; } *
[](int const n) {return n + n; } *
[](int const n) {return std::abs(n); })(-3); // n = "36"
auto c =
[](std::vector<int> const & v) {
return foldl(std::plus<>(), v, 0); } *
[](std::vector<int> const & v) {
return mapf([](int const i) {return i + i; }, v); } *
[](std::vector<int> const & v) {
return mapf([](int const i) {return std::abs(i); }, v); };
auto s = c(vnums); // s = 76