说我有这个功能:
template <typename ...A>
void test(A&& ...a)
{
[=]()
{
};
}
是要转发到lambda还是只是按值复制的参数包?我担心,因为我必须通常明确move()
或forward()
,因为a...
是左值。是一个转发/移动它们的元组中介吗?如果是这样,是否有一种简单的方法可以将元组解压缩到参数包中,而不使用索引技巧?
答案 0 :(得分:7)
一种方法是在Haskell意义上编写一个仿函数。那是一个变异的,它不是Haskell。
编写签名(Ts...)->( ((Ts...)->X) -> X )
的功能。即一个带包的函数,并返回一个函数。返回的函数可以采用一个函数来获取该包并对其进行评估。
template<class...Ts>
auto make_functor(Ts&&...ts); // TODO
一旦我们能够轻松解决您的问题。
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);
});
};
}
test
接受一个包,并返回一个返回该包大小的函数(好吧,对包执行任何操作)。
make_functor
并不容易:基本上,我们编写一个手动lambda,将args存储在元组中,然后在operator()中解析索引技巧。
实际上,我们在手动伪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));
};
}
让参数的值/参考不会丢失!
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);
});
};
}
这确实需要std::experimental::apply
。
如果你想存储 rvalues并将左值作为参考:
unpack_a=delayed_apply(std::tuple<A...>(std::forward<A>(a)...))
如果要存储l和r值:
unpack_a=delayed_apply(std::make_tuple(std::forward<A>(a)...))
正如您所看到的,这种方法可以提供很多控制。
如果您需要std::experimental::apply
,则有参考实施:比我在智能手机上写的任何内容都要好。
请注意,make_functor
可以用delayed_apply
来编写,但相反的是......不是真的。
如果您感到困惑,unpack_a
接受一个lambda并将用于创建unpack_a
的元组解包到其中。基本上我们存储一个整个包的对象,然后当我们需要它在lambda体内时将其解压缩。
如果你希望拆包工作不止一次,那么可能需要处理const和非const以及甚至rvalue重载的更长的delayed_apply
&#34; sometimss和&#34;只有一次&#34;其他时间。它必须返回一个类,而不是lambda。烦人。我认为,使示例代码工作仍然没有编译。
Fortunetally这种事情是写一次,使用很多。
答案 1 :(得分:6)
使用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)...);
}
上述内容适用于Clang,但此GCC似乎存在虚假volatile
限定符的问题。
我们可以在没有bind
的情况下通过在调用std::apply
(C ++ 17)的第二个lambda中捕获tuple
来将元组解压缩到第一个lambda的参数列表中来实现:< / p>
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); };
}
与Clang和GCC合作; apply
是使用您想要避免的索引技巧实现的,但您不会接触它。 mutable
表示第二个lambda的调用运算符是非const的,因此元组元素最终不会获得const
限定。
答案 2 :(得分:3)
首先使用完美转发捕获元组中的参数:
template <typename ...A>
void test(A&& ...a)
{
[tup= std::tuple<A...>(std::forward<A>(a)...)]()
{
//tup should contain the forwarded elements
};
}
然后使用此答案:https://stackoverflow.com/a/7858971/835629在稍后的函数调用中解压缩元组。
//utils
template<int ...>
struct seq { };
template<int N, int ...S>
struct gens : gens<N-1, N-1, S...> { };
template<int ...S>
struct gens<0, S...> {
typedef seq<S...> type;
};
template<typename F, typename T, int ...S>
void unpackTupleToFunction_utils(F func, const T &tup, seq<S...>) {
func(std::get<S>(tup) ...);
}
template<typename F, typename ...Args, int ...S>
void unpackTupleToFunction(F func, const std::tuple<Args...> &tup) {
unpackTupleToFunction_utils(func, tup, typename gens<sizeof...(Args)>::type());
}
最后解压lambda中的元组以便用它调用函数:
template <typename ...Args>
void test(Args&& ...a) {
auto lambda = [tup= std::tuple<Args...>(std::forward<Args>(a)...)]()
{
unpackTupleToFunction(f, tup);
};
lambda();
lambda();
lambda();
}
PS:遗憾的是[a = (std::forward<Args>(a)...)](){};
之类的东西没有编译。
答案 3 :(得分:2)
为什么它不会通过价值?转发仅限于顶级功能。
让我们假设你传递了int
,std::string&
和float&&
,所以你的功能看起来像
void test(int,string&,float&&)
{
[=]()
{
};
}
从那里,匿名lambda将复制 int
,string&
和float&&
的值。引用的副本仍然是副本。
您可以使用tuple
再次打包参数并将其解压缩到lambda中。
如何在lamda中使用元组?
std::apply
的一些非标准实现,并使用元组作为anotehr函数的参数