在 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 提出的观点相关的混淆: 如果 barbas() 中的局部变量(因此被值捕获),则上述 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】:

使用[=][&lt;identifier&gt;] 按值捕获具有创建与捕获的实体完全相同类型的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 默认捕获的缺点?的主要内容,如果未能解决你的问题,请参考以下文章

在 C++11 lambda 中按引用捕获引用

如何在lambda中按值捕获`this`和局部变量?

在 C 中按值调用

Nosql中按值或条件值搜索记录,拥有1亿条记录

按值或 C++11 通用参考传递函子? [复制]

为啥我要在 C 中按值传递函数参数?