Can a C++ lambda constructor argument capture the constructed variable?

后端 未结 2 1727
庸人自扰
庸人自扰 2021-02-19 23:13

The following compiles. But is there ever any sort of dangling reference issue?

    class Foo {
         Foo(std::function fn) { /* etc         


        
相关标签:
2条回答
  • 2021-02-20 00:01

    The lambda expression [&foo](int i){f(i, foo);} will lead compiler to generate a closure class something like this (but not totally correct) :

    class _lambda
    {
        Foo& mFoo; // foo is captured by reference
    
    public:
        _lambda(Foo& foo) : mFoo(foo) {}
    
        void operator()(int i) const
        {
           f(i, mFoo);
        }
    };
    

    Therefore, the declaration Foo foo([&foo](int i){f(i, foo);}); is treated as Foo foo(_lambda(foo));. Capturing foo itself when constructing does not has problem in this situation because only its address is required here (References are usually implemented via pointers).

    The type std::function<void(int)> will internally copy construct this lambda type, which means that Foo's constructor argument fn holds a copy of _lambda object (that holds a reference (i.e., mFoo) to your foo).

    These implies that dangling reference issue may arise in some situations, for example:

    std::vector<std::function<void(int)>> vfn; // assume vfn live longer than foo
    
    class Foo {
         Foo(std::function<void(int)> fn) { vfn.push_back(fn); }
    }
    
    void f(int i, Foo& foo) { /* stuff with i and foo */ }
    
    Foo foo([&foo](int i){f(i, foo);});
    
    ....
    
    void ff()
    {
        // assume foo is destroyed already,
        vfn.pop_back()(0); // then this passes a dangling reference to f.
    }
    
    0 讨论(0)
  • 2021-02-20 00:16

    But is there ever any sort of dangling reference issue?

    That depends entirely on what you're doing with Foo. Here's an example that would have dangling reference issues:

    struct Foo {
         Foo() = default;
         Foo(std::function<void(int)> fn) : fn(fn) { }
         std::function<void(int)> fn;
    }
    
    Foo outer;
    {
        Foo inner([&inner](int i){f(i, inner);});
        outer = inner;
    }
    outer.fn(42); // still has reference to inner, which has now been destroyed
    
    0 讨论(0)
提交回复
热议问题