在C++11中使用lambda默认值捕获或引用捕获的缺点

Disadvantage of using lambda default capture by value or reference in C++11?

本文关键字:缺点 引用 默认值 C++11 lambda      更新时间:2023-10-16

在C++11中使用lambda默认值捕获([=])或引用捕获([&])有哪些陷阱?

我知道一些陷阱,比如:

  • 如果从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:
提出的观点有关的混淆如果barbas()中的局部变量(因此由值捕获),则上述lambda不会编译,因为默认情况下,由值捕获的变量是const,除非显式指定lambda为可变。所以,仔细想想,很明显,bar并没有被复制,但在阅读或编写代码时,它很容易被遗忘。

它与之间的比较具有完全相同的优点和缺点

int value(const T x) { ... }
int value(T& x) { ... }

使用[=][<identifier>]按值捕获具有创建与捕获的实体类型完全相同的lambda成员的效果,包括constness,例如。当按值捕获const int时,即使lambda调用运算符是可变的,结果成员也可以不发生突变。

const int i = 1;
[=] () mutable { ++i; }(); // Error: increment of read-only variable.

这可以通过使用C++14的初始化捕获表达式来解决:

[i = i] () mutable { ++i; }(); // Ok

按值捕获涉及复制关闭的值,因此可能意味着更多的内存消耗和对该副本的更多处理。