第一次尝试:
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);
}
在 C++14 中。现在,这并不完美,因为该模式在 C++ 中并不能很好地工作。
要完美地做到这一点,我们必须看看组合编程。在这里,函数与抽象的参数堆栈交互。每个函数都会从堆栈中弹出一些参数,然后再弹出一些参数。
这将允许您这样做:
compose( print_coord, get_x, get_y )
其中get_x 和get_y 只消耗一个坐标,而print_coord 获取两个坐标并打印它们。
要在 C++ 中模拟这一点,我们需要一些花哨的机器。函数将返回 tuples(或 tuple-likes?),这些值将在逻辑上“推入参数堆栈”。
函数也会消耗这个参数堆栈中的东西。
在每次调用时,我们解包当前的参数元组,找到可以调用函数的最长集合,调用它,获取它的返回值,如果是元组则解包,然后粘贴任何这样的返回值返回参数堆栈。
为了让这个更高级的compose 与自身组合,它需要 SFINAE 检查,并且它需要能够接受一个可调用对象和一个参数元组,并找到正确数量的参数来调用可调用对象,加上剩余的参数。
这是一个棘手的元编程,我不会在这里做。
第二部分,因为第一次错过,长这样:
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;
};
}
这不使用类型擦除。
测试代码:
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";
打印:
2
6
18
54