繁体   English   中英

C ++ 11 lambdas:成员变量捕获陷阱

[英]C++11 lambdas: member variable capture gotcha

考虑以下代码:

#include <memory>
#include <iostream>

class A
{
public:
    A(int data) : data_(data)
    { std::cout << "A(" << data_ << ")" << std::endl; }
    ~A() { std::cout << "~A()" << std::endl; }
    void a() { std::cout << data_ << std::endl; }
private:
    int data_;
};

class B
{
public:
    B(): a_(new A(13)) { std::cout << "B()" << std::endl; }
    ~B() { std::cout << "~B()" << std::endl; }
    std::function<void()> getf()
    {
        return [=]() { a_->a(); };
    }
private:
    std::shared_ptr<A> a_;
};

int main()
{
    std::function<void()> f;
    {
        B b;
        f = b.getf();
    }
    f();
    return 0;
}

这看起来我正在按值捕获a_共享指针,但是当我在Linux(GCC 4.6.1)上运行它时,会打印出来:

A(13)
B()
~B()
~A()
0

显然,0是错误的,因为A已经被破坏了。 看起来this实际上已被捕获并用于查找此 - this->a_ 当我将捕获列表从[=]更改为[=,a_]时,我的怀疑得到了确认。 然后打印正确的输出,并且对象的生命周期符合预期:

A(13)
B()
~B()
13
~A()

问题:

此行为是由标准,实现定义还是未定义指定的? 或者我疯了,这是完全不同的东西?

此行为是否由标准指定

是。 捕获成员变量总是通过捕获this来完成; 它是访问成员变量的唯一方法。 在成员函数的范围内, a_等效于(*this).a_ Lambdas也是如此。

因此,如果您使用this (隐式或显式),那么您必须确保在lambda实例存在时对象保持活动状态。

如果要按值捕获它,则必须明确这样做:

std::function<void()> getf()
{
    auto varA = a_;
    return [=]() { varA->a(); };
}

如果您需要规格报价:

lambda-expression的复合语句产生函数调用操作符的函数体(8.4),但是为了查找名称(3.4),确定它的类型和值(9.3.2)并转换id-expressions引用使用(* this)(9.3.1)将非静态类成员转换为类成员访问表达式,在lambda表达式的上下文中考虑复合语句。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM