【发布时间】:2016-04-06 17:02:21
【问题描述】:
在 C++11 中使用 lambda 默认捕获按值 ([=]) 或按引用 ([&]) 有哪些缺陷?
我知道一些陷阱,例如:
- 如果从 lambda 创建的闭包的生命周期超过局部变量的生命周期,闭包中的引用将悬空?
默认按值捕获有什么缺点吗?
【问题讨论】:
在 C++11 中使用 lambda 默认捕获按值 ([=]) 或按引用 ([&]) 有哪些缺陷?
我知道一些陷阱,例如:
默认按值捕获有什么缺点吗?
【问题讨论】:
我认为你提到的悬空引用问题是主要缺陷。
另外一件有时被忽略的事情是,即使在成员函数中使用按值捕获的 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 不是复制的,但是在阅读或编写代码时很容易忘记。
【讨论】:
它的优点和缺点与以下之间的比较完全相同:
int value(const T x) { ... }
int value(T& x) { ... }
【讨论】:
int value(const T x) { ... } 等函数的使用完全不同。如需参考,请参阅 - stackoverflow.com/questions/5501959/…
使用[=] 或[<identifier>] 按值捕获具有创建与捕获的实体完全相同类型的lambda成员的效果,包括常量,例如,当按值捕获 const int 时,即使 lambda 调用运算符是可变的,结果成员也不能被改变。
const int i = 1;
[=] () mutable { ++i; }(); // Error: increment of read-only variable.
这可以使用 C++14 的初始化捕获表达式来解决:
[i = i] () mutable { ++i; }(); // Ok
【讨论】:
按值捕获涉及复制已关闭的值,因此可能意味着该副本需要更多的内存消耗和更多的处理。
【讨论】: