在 C++11 中按值或引用使用 lambda 默认捕获的缺点?
Posted
技术标签:
【中文标题】在 C++11 中按值或引用使用 lambda 默认捕获的缺点?【英文标题】:Disadvantage of using lambda default capture by value or reference in C++11? 【发布时间】:2016-04-06 17:02:21 【问题描述】:在 C++11 中使用 lambda 默认捕获按值 ([=]
) 或按引用 ([&]
) 有哪些缺陷?
我知道一些陷阱,例如:
如果从 lambda 创建的闭包的生命周期超过局部变量的生命周期,闭包中的引用将悬空?默认按值捕获有什么缺点吗?
【问题讨论】:
【参考方案1】:我认为你提到的悬空引用问题是主要缺陷。
另外一件有时被忽略的事情是,即使在成员函数中使用按值捕获的 lambda,它也不会创建已使用成员变量的副本,而只会创建this
指针。
首先,这意味着您再次面临悬空指针问题,其次,您可能会意外修改 lambda 范围之外的变量,即使看起来您只是在修改本地副本。
例如这将打印 0 1 1
而不是 0 1 0
struct Foo
int bar=0;
int bas()
auto inc = [=]()
bar++; //this is equivalent to this->bar++
return bar;
;
return inc();
;
int main()
Foo foo;
std::cout << foo.bar <<" ";
std::cout << foo.bas() << " ";
std::cout << foo.bar << std::endl;
编辑:
只是为了避免与@Snps 提出的观点相关的混淆:
如果 bar
是 bas()
中的局部变量(因此被值捕获),则上述 lambda 不会编译,因为按值捕获的变量默认为 const,除非您明确指定 lambda 为可变。
所以如果你仔细想想,很明显 bar 不是复制的,但是在阅读或编写代码时很容易忘记。
【讨论】:
嗨迈克,谢谢你的回答,据我了解,捕获值类型成为闭包类的数据成员,引用不会成为闭包类的数据成员。请确认。 @Ajayyadav:嗯,它也变成了一个数据成员,但是是引用类型【参考方案2】:它的优点和缺点与以下之间的比较完全相同:
int value(const T x) ...
int value(T& x) ...
【讨论】:
好答案,我只想补充一点,lambdas 是对象。因此,当按值捕获时,重复使用 lambda 对象将使用它的内部状态。这与int value(const T x) ...
等函数的使用完全不同。如需参考,请参阅 - ***.com/questions/5501959/…
@Jendas:我不确定您的引用是否正确,但您是正确的,该值是在创建 lambda 时而不是在调用时捕获的。
您好 Bill,感谢您的回答,默认值类型被捕获为 const 和引用类型非 const
是的,它并没有完全解决问题,但它是我找到的关于 lambdas 内部状态的最接近的信息。【参考方案3】:
使用[=]
或[<identifier>]
按值捕获具有创建与捕获的实体完全相同类型的lambda成员的效果,包括常量,例如,当按值捕获 const int
时,即使 lambda 调用运算符是可变的,结果成员也不能被改变。
const int i = 1;
[=] () mutable ++i; (); // Error: increment of read-only variable.
这可以使用 C++14 的初始化捕获表达式来解决:
[i = i] () mutable ++i; (); // Ok
【讨论】:
【参考方案4】:按值捕获涉及复制已关闭的值,因此可能意味着该副本需要更多的内存消耗和更多的处理。
【讨论】:
以上是关于在 C++11 中按值或引用使用 lambda 默认捕获的缺点?的主要内容,如果未能解决你的问题,请参考以下文章