I recently read about a neat trick regarding overloading of lambda expressions in C++ in a blog post by Dave Abrahams here. This technique was originally described by Mathias Gaunard. I want to do a short post about it here so I don't forget it.
In C++11, it is possible to overload lambda expressions by creating a helper function object class that inherits from the function objects the compiler generates for the lambda expressions and then pulling in the lambda expressions' operator() via using declarations.
Simple Example
Here's a simple console application that demonstrates this for two lambda expressions:
#include <iostream> template <class F1, class F2> struct overload_set : F1, F2 { overload_set(F1 f1, F2f2) : F1(f1), F2(f2) {} using F1::operator(); using F2::operator(); }; template <class F1, class F2> overload_set<F1, F2> overload(F1 f1, F2 f2) { return overload_set<F1, F2>(f1, f2); } int main(int argc, const char* argv[]) { auto f = overload ( []() { return 1; }, [](int x) { return x + 1; } ); int x = f(); int y = f(2); std::cout << "x = " << x << ", y = " << y << std::endl; return 0; }
The overload function in this example returns a function object called overload_set that inherits from the two function objects created by the compiler for the two lambda expressions passed to it. Note that you don't necessarily have to use lambda expressions. Any function object class that exposes an operator() can be used.
The two operator() are then pulled into the overload_set class via a using declaration. So client code now sees a function object class that provides two operator(), in this case, one that has no arguments and one that has an integer argument. When calling through the function object regular function overloading comes into play and the compiler chooses the correct function to call depending on the supplied arguments.
Variadic Template Implementation
Here's a more general implementation of the technique described above using variadic templates. If you're not yet familiar with C++11 variadic templates, the Wikipedia page on the subject is pretty good.
template <class... Fs> struct overload_set; template <class F1, class... Fs> struct overload_set<F1, Fs...> : F1, overload_set<Fs...>::type { typedef overload_set type; overload_set(F1 head, Fs... tail) : F1(head), overload_set<Fs...>::type(tail...) {} using F1::operator(); using overload_set<Fs...>::type::operator(); }; template <class F> struct overload_set<F> : F { typedef F type; using F::operator(); }; template <class... Fs> typename overload_set<Fs...>::type overload(Fs... x) { return overload_set<Fs...>(x...); }
This is quite a neat trick, even though admittedly it probably won't be useful very often. It might come in handy for certain generic algorithms, for example to implement compile-time double dispatch.
Pingback: C++11 "overloaded lambda" with variadic template and variable capture - BlogoSfera
isn't it
auto f = overload_set
instead of
auto f = overload ?
though I haven't tried compiler yet..
No, overload is correct. Note that overload_set is the return type of function overload, which is what gets called here.
Shouldn't we forward arguments?
return overload_set(std::forward(x)...);
Yes, you're right.
The final TMP example you gave is really useful for the set operations in std::algorithm. Set_union, set_intersection, includes, etc. can work between maps, sets, and sorted vectors, but if you have different tuples (like map v set) then you need one overload for
operator()(pair, string)
and one for
operator()(string, pair)
Just curious, what's the purpose of using `type` in the `overload_set` class? It can be implemented in another fashion. See here
https://godbolt.org/z/188vs1ba3
Pingback: C++11 "lambda sobrecargada" con plantilla variƔdica y captura de variables - Fallosweb.com