说我有这个功能:
template <typename ...A>
void test(A&& ...a)
{
[=]()
{
};
}
Run Code Online (Sandbox Code Playgroud)
参数包是转发到lambda还是只是按值复制?我担心,因为我必须明确move()或forward()通常,和a...左值.是一个转发/移动它们的元组中介吗?如果是这样,是否有一种简单的方法可以将元组解压缩到参数包中,而不使用索引技巧?
一种方法是在Haskell意义上编写一个仿函数.那是一个变异的,它不是非常Haskell.
写一个签名函数(Ts...)->( ((Ts...)->X) -> X ).即一个带包的函数,并返回一个函数.返回的函数可以采用一个函数来获取该包并对其进行评估.
template<class...Ts>
auto make_functor(Ts&&...ts); // TODO
Run Code Online (Sandbox Code Playgroud)
一旦我们有了,我们就可以轻松解决您的问题.
template<class ...A>
auto test(A&& ...a) {
return [unpack_a=make_functor(std::forward<A>(a)...)]() mutable
{
return unpack_a([&](auto&&...a){
// here you have access to a...
return sizeof...(a);
});
};
}
Run Code Online (Sandbox Code Playgroud)
test 接受一个包,并返回一个返回该包大小的函数(好吧,对包执行任何操作).
make_functor 是不容易的:基本上,我们写了一个手动拉姆达,存储在一个元组中的指定参数时,在操作拆包沉思索引绝招().
实际上,我们在手动伪lambda类中进行一次包存储和解包,然后再重新使用它.
第二个想法,最好是编写一个延迟的应用程序,它接受一个元组,存储它,然后再使用std::apply.
template<class...Ts>
auto delayed_apply(std::tuple<Ts...> tup){
return [tup=std::move(tup)](auto&&f)->decltype(auto) mutable{
return std::experimental::apply(decltype(f)(f), std::move(tup));
};
}
Run Code Online (Sandbox Code Playgroud)
这让参数的值/参考不会丢失!
template<class ...A>
auto test(A&& ...a) {
return [unpack_a=delayed_apply(std::forward_as_tuple(std::forward<A>(a)...))]() mutable
{
return unpack_a([&](auto&&...a){
// here you have access to a...
return sizeof...(a);
});
};
}
Run Code Online (Sandbox Code Playgroud)
这确实需要std::experimental::apply.
如果要存储 rvalues 并将左值作为参考:
unpack_a=delayed_apply(std::tuple<A...>(std::forward<A>(a)...))
Run Code Online (Sandbox Code Playgroud)
如果要存储l和r值:
unpack_a=delayed_apply(std::make_tuple(std::forward<A>(a)...))
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,这种方法可以提供很多控制.
如果你需要std::experimental::apply,有参考实现:比我在智能手机上写的任何东西更好.
注意,make_functor可以用delayed_apply,但相反的是......不是真的.
如果您感到困惑,请unpack_a使用lambda并将用于创建的元组解压缩unpack_a.基本上我们存储一个整个包的对象,然后当我们需要它在lambda体内时解压缩它.
delayed_apply如果你希望拆包工作"不止一次"sometimss和"仅一次"其他时间,则可能需要更长的处理const和非const以及甚至rvalue重载.它必须返回一个类,而不是lambda.烦人.我认为,使示例代码工作仍然没有编译.
Fortunetally这种东西是写一次,使用很多.
可以完成的少数剩余有用的事情之一std::bind.捕获是由执行的bind,捕获的值作为参数传递给无捕获的通用lambda:
template <typename... A>
auto test(A&&... a)
{
auto f = [](auto&&... a)
{
// use a...
};
return std::bind(f, std::forward<A>(a)...);
}
Run Code Online (Sandbox Code Playgroud)
上面的内容适用于Clang,但是这个GCC似乎与虚假的volatile限定符有关.
我们可以在没有bind捕获tuple第二个lambda来调用std::apply(C++ 17)将元组解压缩到第一个lambda的参数列表中来实现它:
template <typename... A>
auto test(A&&... a)
{
auto f = [](auto&&... a)
{
// use a...
};
return [f, tup = std::make_tuple(std::forward<A>(a)...)]() mutable { std::apply(f, tup); };
}
Run Code Online (Sandbox Code Playgroud)
与Clang和GCC合作; apply使用您想要避免的索引技巧实现,但您不会接触它.这mutable意味着第二个lambda的调用操作符是非const的,因此元组元素最终不会获得const限定.