问题
This is more a kind of theoretical question. Is it possible in C++11 to combine functions into a new function? For example :
auto f = [](int i){return i * 2;};
auto g = [](int i){return i + 10;};
So this works:
auto c = f(g(20)); // = 60
But I want an object that stores the combination, like
auto c = f(g);
std::cout << c(20) << std::endl; //prints 60
Edit:
Additionally what i want to create is a function a, which you can give a function b and an int n
, and which returns the n'th combination of the given function b. For example (not compilable)
template<typename T>
auto combine(T b, int i) -> decltype(T)
{
if (i == 0)
return b;
return combine(b, i - 1);
}
auto c = combine(f, 2); //c = f(f(f(int)))
回答1:
You can write something along the lines of:
#include <functional>
#include <iostream>
template<class F>
F compose(F f, F g)
{
return [=](int x) { return f(g(x)); };
}
int main()
{
std::function<int (int)> f = [](int i) { return i * 2; };
std::function<int (int)> g = [](int i) { return i + 10; };
auto c = compose(f, g);
std::cout << c(20) << '\n'; // prints 60
}
The code can be simply extended to cover the second half of the question:
template<class F>
F compose(F f, unsigned n)
{
auto g = f;
for (unsigned i = 0; i < n; ++i)
g = compose(g, f);
return g;
}
int main()
{
std::function<int (int)> h = [](int i) { return i * i; };
auto d = compose(h, 1);
auto e = compose(h, 2);
std::cout << d(3) << "\n" // prints 81
<< e(3) << "\n"; // prints 6561
}
NOTE. Here using std::function
. It isn't a lambda but wraps a lambda with a performance cost.
回答2:
A first attempt:
template<class First, class Second>
auto compose( Second&& second, First&& first ) }
return [second = std::forward<Second>(second), first=std::forward<First>(first)]
(auto&&...args)->decltype(auto) {
return second( first( decltype(args)(args)... ) );
};
}
template<class A, class B, class...Rest>
auto compose(A&& a, B&& b, Rest&&... rest) {
return compose( compose(std::forward<A>(a), std::forward<B>(b)), std::forward<Rest>(rest)... );
}
template<class A>
std::decay_t<A> compose(A&& a) {
return std::forward<A>(a);
}
in C++14. Now, this isn't perfect, as the pattern doesn't work all that well in C++.
To do this perfectly, we'd have to take a look at compositional programming. Here, functions interact with an abstract stack of arguments. Each function pops some number of arguments off the stack, then pops some number back on.
This would allow you do do this:
compose( print_coord, get_x, get_y )
where get_x
and get_y
consume nothing but return a coordinate, and print_coord
takes two coordinates and prints them.
To emulate this in C++, we need some fancy machinery. Functions will return tuple
s (or tuple-likes?), and those values will be "pushed onto the argument stack" logically.
Functions will also consume things off this argument stack.
At each invocation, we unpack the current tuple of arguments, find the longest collection that the function can be called with, call it, get its return value, unpack it if it is a tuple, and then stick any such returned values back on the argument stack.
For this more advanced compose
to compose with itself, it then needs SFINAE checks, and it needs to be able to take a invokable object and a tuple of arguments and find the right number of arguments to call the invokable object with, plus the left-over arguments.
This is a tricky bit of metaprogramming that I won't do here.
The second part, because I missed it the first time, looks like:
template<class F>
auto function_to_the_power( F&& f, unsigned count ) {
return [f=std::forward<F>(f),count](auto&& x)
-> std::decay_t< decltype( f(decltype(x)(x)) ) >
{
if (count == 0) return decltype(x)(x);
auto r = f(decltype(x)(x));
for (unsigned i = 1; i < count; ++i) {
r = f( std::move(r) );
}
return r;
};
}
This uses no type erasure.
Test code:
auto f = [](int x){ return x*3; };
auto fs = std::make_tuple(
function_to_the_power( f, 0 ),
function_to_the_power( f, 1 ),
function_to_the_power( f, 2 ),
function_to_the_power( f, 3 )
);
std::cout << std::get<0>(fs)(2) << "\n";
std::cout << std::get<1>(fs)(2) << "\n";
std::cout << std::get<2>(fs)(2) << "\n";
std::cout << std::get<3>(fs)(2) << "\n";
prints:
2 6 18 54
来源:https://stackoverflow.com/questions/33934687/is-it-possible-in-c11-to-combine-functions-into-a-new-function