Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
291 views
in Technique[技术] by (71.8m points)

c++ - C++11 lambdas: member variable capture gotcha

Consider this code:

#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;
}

Here it looks like I'm capturing a_ shared pointer by value, but when I run it on Linux (GCC 4.6.1), this is printed:

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

Obviously, 0 is wrong, because A is already destroyed. It looks like this is actually captured and is used to look up this->a_. My suspicion is confirmed when I change the capture list from [=] to [=,a_]. Then the correct output is printed and the lifetime of the objects is as expected:

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

The question:

Is this behaviour specified by the standard, implementation-defined, or undefined? Or I'm crazy and it's something entirely different?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Is this behaviour specified by the standard

Yes. Capturing member variables is always done via capturing this; it is the only way to access a member variable. In the scope of a member function a_ is equivalent to (*this).a_. This is true in Lambdas as well.

Therefore, if you use this (implicitly or explicitly), then you must ensure that the object remains alive while the lambda instance is around.

If you want to capture it by value, you must explicitly do so:

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

If you need a spec quote:

The lambda-expression’s compound-statement yields the function-body ( 8.4 ) of the function call operator, but for purposes of name lookup (3.4), determining the type and value of this (9.3.2) and transforming id-expressions referring to non-static class members into class member access expressions using (*this) ( 9.3.1 ), the compound-statement is considered in the context of the lambda-expression.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

2.1m questions

2.1m answers

60 comments

57.0k users

...