0

I have a class which has a public std::function member like this:

class B
{
public:
    B(std::function<void(void)> _func = NULL) : m_function(_func) { }
    std::function<void()> m_function;
};

I have a class X with a member function SomeFunction:

class X
{
public:
    void SomeFunction(const std::string & _s)
    {
        //...
    }
};

I also have a class A which returns an object of type B, like this:

class A
{
private:
    X x;
public:
    B GetObjectB()
    {
        std::string local = "abc";
        return B(([&]() -> void { x->SomeFunction(local); }))
    }
};

I then execute

A a;
B b = a.GetObjectB();
b.m_function();

The code works fine until the function SomeFunction is called, and the input arguments _s does not have a value although have passed local as its input argument.

What am I doing wrong here?

2 Answers 2

3

You're telling your lambda to default capture by reference ([&]), and it's capturing the reference to local that's on the stack. As a stack variable, this is deallocated when the scope exits, so the returned object's my_function has captured a dangling reference. Try [=].

Sign up to request clarification or add additional context in comments.

Comments

3

This piece of code

std::string local="abc";
return B(([&]()->void{x->SomeFunction(local);}))

captures all used variables by reference, and returns the function. Among the captured variables is the local variable local, whose lifetime ends when the scope exits, i.e. when returning from the function. So the local variable doesn't exist anymore when the lambda will be executed later, which means that the captured reference is invalid ("dangling reference").

Quoting from http://en.cppreference.com/w/cpp/language/lambda:

Dangling references

If an entity is captured by reference, implicitly or explicitly, and the function call operator of the closure object is invoked after the entity's lifetime has ended, undefined behavior occurs. The C++ closures do not extend the lifetimes of the captured references.

This paragraph indicates that in other languages with closure support (e.g. in JavaScript), the lifetime of variables captured by reference are extended, which is not the case in C++.

The same problem exists for the variable x which is a member variable of the class A the function was invoked on. While you probably don't have a problematic code in your test, this might become a problem, too:

B b;

// some other context
{
    A a;
    b = a.GetObjectB();
}

// The lambda previously returned from a.GetObjectB() is now
// stored in b. But it still refers to the now dead a.x!

b.m_function();    // BOOM!

An easy fix is to simply not capture all variables by reference but by value, so the variable gets copied into the lambda instance:

std::string local="abc";
return B(([=]()->void{x->SomeFunction(local);}))

Possible workarounds when you don't want to copy the value are discussed in my answer to a very similar question: https://stackoverflow.com/a/20928847/592323.

2 Comments

It's doubly uncomfortable because there's also an implicit assumption that the A object is alive when the function is called...
@KerrekSB Thanks for pointing this out, I added it to my answer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.