将 lambda 作为参数传递 - 通过引用或值?
Posted
技术标签:
【中文标题】将 lambda 作为参数传递 - 通过引用或值?【英文标题】:passing lambda as argument - by reference or value? 【发布时间】:2017-07-11 10:04:12 【问题描述】:我编写了一个模板代码,它接受一个函子作为参数,经过一些处理后,执行它。尽管其他人可能会将该函数传递给 lambda、函数指针甚至 std::function
,但它主要用于 lambda(不是我禁止其他格式)。我想问我应该如何取那个 lambda - 按值?引用?或者别的什么。
示例代码-
#include <iostream>
#include <functional>
using namespace std;
template<typename Functor>
void f(Functor functor)
functor();
void g()
cout << "Calling from Function\n";
int main()
int n = 5;
f([]()cout << "Calling from Temp Lambda\n";);
f([&]()cout << "Calling from Capturing Temp Lambda\n"; ++n;);
auto l = []()cout << "Calling from stored Lambda\n";;
f(l);
std::function<void()> funcSTD = []() cout << "Calling from std::Function\n"; ;
f(funcSTD);
f(g);
在上面的代码中,我可以选择其中任何一个 -
template<typename Functor>
void f(Functor functor)
template<typename Functor>
void f(Functor &functor)
template<typename Functor>
void f(Functor &&functor)
什么是更好的方法,为什么?这些有什么限制吗?
【问题讨论】:
恕我直言,按值传递。 我一直看到仿函数模板对象按值传递,但我不知道为什么;好问题。 相关/欺骗:***.com/questions/8196345/… 【参考方案1】:作为一个可能的缺点,请注意,如果 lambda 不可复制,则无法通过复制传递。如果你能侥幸逃脱,通过副本就可以了。 举个例子:
#include<memory>
#include<utility>
template<typename F>
void g(F &&f)
std::forward<F>(f)();
template<typename F>
void h(F f)
f();
int main()
auto lambda = [foo=std::make_unique<int>()]();
g(lambda);
//h(lambda);
在上面的 sn-p 中,lambda
不可复制,因为 foo
。由于 std::unique_ptr
的复制构造函数被删除,它的复制构造函数被删除。
另一方面,F &&f
接受左值和右值引用,因为它是转发引用以及 const 引用。
换句话说,如果您想多次重用相同的 lambda 作为参数,如果您的函数通过复制获取对象并且您必须移动它,因为它不可复制(好吧,实际上您可以,这只是将它包装在一个通过引用捕获外部的 lambda 中)。
【讨论】:
@NathanOliver 当然,但你不能再重用 lambda。如果你想将它传递给示例中的两个函数,这是不可能的。 @skypjack 后续问题 - 除了定义两个类似的重载之外,是否有可能在这样的场景中回退到可移动函子.. @AbhinavGauniyal 通过定义两个执行完全相同工作的重载,只能不使用转发引用?我没有得到确切的好处。我是不是误会了什么? @skypjack 我的意思是有两个h()
的定义,其中第一个是值,第二个是&&
.. 这样编译就不会失败。
@Nik-Lz 不是最好的定义,但接近现实。 ;-)【参考方案2】:
由于 lambda 表达式可以有自己的字段(如类),因此复制/使用引用会导致不同的结果。这是一个简单的例子:
template<class func_t>
size_t call_copy(func_t func)
return func(1);
template<class func_t>
size_t call_ref(func_t& func)
return func(1);
int main()
auto lambda = [a = size_t0u] (int val) mutable
return (a += val);
;
lambda(5);
// call_ref(lambda); – uncomment to change result from 5 to 6
call_copy(lambda);
std::cout << lambda(0) << std::endl;
return 0;
顺便说一句,如果你想通过性能来判断它实际上没有区别,lambda 非常小并且易于复制。
另外——如果你想将 lambda 作为参数(不是包含它的变量)传递,你应该使用转发引用以便它可以工作。
【讨论】:
“由于 lambda 表达式可以有自己的字段”和“lambda 非常小且易于复制”是两个相互矛盾的陈述。 lambda 有多大取决于这些字段有多大。可以创建任意大且非常难以复制的 lambda。以上是关于将 lambda 作为参数传递 - 通过引用或值?的主要内容,如果未能解决你的问题,请参考以下文章