¿Qué tal este?
#include <cstdio>
#include <functional>
template <typename F, typename F_ret, typename... F_args,
typename G, typename G_ret, typename... G_args>
std::function<G_ret (F_args...)>
composer(F f, F_ret (F::*)(F_args...) const ,
G g, G_ret (G::*)(G_args...) const)
{
// Cannot create and return a lambda. So using std::function as a lambda holder.
std::function<G_ret (F_args...)> holder;
holder = [f, g](F_args... args) { return g(f(args...)); };
return holder;
}
template<typename F, typename G>
auto operator >> (F f, G g)
-> decltype(composer(f, &F::operator(), g, &G::operator()))
{
return composer(f, &F::operator(), g, &G::operator());
}
int main(void)
{
auto l1 = [](int i , int j) { return i + j; };
auto l2 = [](int a) { return a*a; };
printf("%d\n", (l1 >> l2 >> l2)(2, 3)); // prints 625
return 0;
}
Editar:
Aquí hay un código mejorado con soporte para punteros de función libres y los punteros de función miembro. También tengo un código de prueba. Tenga cuidado con el número de llamadas a funciones virtuales que tienen lugar cuando ejecuta dichos objetos std :: function profundamente compuestos. Creo que hay una llamada de función virtual por operador() de un objeto std :: function. La asignación de memoria y la desasignación es otra cosa que debes tener en cuenta.
#include <cstdio>
#include <functional>
template <typename F, typename F_ret, typename... F_args,
typename G, typename G_ret, typename... G_args>
std::function<G_ret (F_args...)>
composer(F f, F_ret (F::*)(F_args...) const ,
G g, G_ret (G::*)(G_args...) const)
{
// Cannot create and return a lambda. So using std::function as a lambda holder.
std::function<G_ret (F_args...)> holder;
holder = [f, g](F_args... args) { return g(f(args...)); };
return holder;
}
template<typename F_ret, typename... F_args>
std::function<F_ret (F_args...)>
make_function (F_ret (*f)(F_args...))
{
// Not sure why this helper isn't available out of the box.
return f;
}
template<typename F, typename F_ret, typename... F_args>
std::function<F_ret (F_args...)>
make_function (F_ret (F::*func)(F_args...), F & obj)
{
// Composing a member function pointer and an object.
// This one is probably doable without using a lambda.
std::function<F_ret (F_args...)> holder;
holder = [func, &obj](F_args... args) { return (obj.*func)(args...); };
return holder;
}
template<typename F, typename F_ret, typename... F_args>
std::function<F_ret (F_args...)>
make_function (F_ret (F::*func)(F_args...) const, F const & obj)
{
// Composing a const member function pointer and a const object.
// This one is probably doable without using a lambda.
std::function<F_ret (F_args...)> holder;
holder = [func, &obj](F_args... args) { return (obj.*func)(args...); };
return holder;
}
template<typename F, typename G>
auto operator >> (F f, G g)
-> decltype(composer(f, &F::operator(), g, &G::operator()))
{
return composer(f, &F::operator(), g, &G::operator());
}
// This one allows a free function pointer to be the second parameter
template<typename F, typename G_ret, typename... G_args>
auto operator >> (F f, G_ret (*g)(G_args...))
-> decltype(f >> make_function(g))
{
return f >> make_function(g);
}
// This one allows a free function pointer to be the first parameter
template<typename F, typename G_ret, typename... G_args>
auto operator >> (G_ret (*g)(G_args...), F f)
-> decltype(make_function(g) >> f)
{
return make_function(g) >> f;
}
// Not possible to have function pointers on both sides of the binary operator >>
int increment(int i) {
return i+1;
}
int sum(int i, int j) {
return i+j;
}
struct math {
int increment (int i) {
return i+1;
}
int sum (int i, int j) const {
return i+j;
}
};
int main(void)
{
auto l1 = [](int i , int j) { return i + j; };
auto l2 = [](int a) { return a*a; };
auto l3 = l1 >> l2 >> l2 >> increment; // does 11 allocs on Linux
printf("%d\n", l3(2, 3)); // prints 626
printf("%d\n", (sum >> l2)(3, 3)); // prints 36
math m;
printf("%d\n",
(make_function(&math::sum, m) >> make_function(&math::increment, m))(2, 3)); // prints 6
return 0;
}
Funciona si reemplaza 'auto' por' std :: function <...> '. La plantilla solo coincide con el tipo exacto, pero las lambdas no son del tipo 'std :: function'. –
@Kerrek: La pregunta es sobre cómo hacer que el operador sobrecargado funcione tanto para 'auto' como' std :: function'. De hecho, parte de la intención es no requerir el reemplazo de 'auto' con' std :: function'. – sinharaj