Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can the [this] pointer captured by a lambda be invalidated before the lambda runs?

Tags:

c++

lambda

this

Suppose that we have a STL container with some objects, and these objects can post functions to a queue to be executed later. But before these functions get executed, the container gets modified in such a way that pointers pointing to that object are invalidated. Let me illustrate with an example:

#include <vector>
#include <functional>
class Class_A
{
public:
    std::function<void()> getFunctionToRunLater()
    {
        return [this] () { somethingToDo(); moreThingsToDo(); };
        // Returns a lambda function that captures the this pointer,
        // so it can access the object's methods and variables.
    }

    void somethingToDo();
    void moreThingsToDo();
}

int main()
{
    std::vector<Class_A> vec;
    vec.push_back(Class_A());

    std::function<void()> pendingFunction = vec.back().getFunctionToRunLater();

   // More code...

   pendingFunction();
}

Everything fine, right? We get a function the object wants to run and, after some logic, we execute that function. This represents posting functions to a queue and them execute all functions in the queue. But now look at this one:

int main()
{
    std::vector<Class_A> vec;
    vec.push_back(Class_A());

    std::function<void()> pendingFunction = vec.back().getFunctionToRunLater();

   // More code...

   vec.reserve(1000);
   // This will surely reallocate the vector, invalidating all pointers.

   pendingFunction();
   // And now my program is going straight down to hell, right?
}

Is my assumption correct? What will happen if the lambda doesn't capture anything at all, will the program still be logically broken? And what about if the lambda doesn't capture the this pointer, but rather some other class field specifically?

like image 931
P. Rodríguez Avatar asked Oct 06 '19 13:10

P. Rodríguez


People also ask

What does it mean to lambda capture this?

In Short. A lambda defined inside a non-static member function can directly access the members of the current object (or its copy) via an appropriate capture clause. But how the current object can be captured has gone through some changes since C++11.

What happens when a lambda goes out of scope?

When a lambda object outlives one of its reference-captured objects, execution of the lambda object's function call operator results in undefined behavior once that reference-captured object is accessed. Therefore, a lambda object must not outlive any of its reference-captured objects.

Is lambda function a function pointer?

A lambda expression with an empty capture clause is convertible to a function pointer. It can replace a stand-alone or static member function as a callback function pointer argument to C API.

How does lambda work in c++?

A lambda can introduce new variables in its body (in C++14), and it can also access, or capture, variables from the surrounding scope. A lambda begins with the capture clause. It specifies which variables are captured, and whether the capture is by value or by reference.


1 Answers

The existing answer already mentions that the pointer can be invalidated. One way to avoid the problem is, as already mentioned, changing the ownership of *this by either shared_ptr, unique_ptr or a copy. However, this comes at extra cost (dynamic allocation or extra copy) and sometimes is simply not possible (non-copyable types).

Instead, I would suggest a design that doesn't lead to this problem in the first place, i.e. not making the this pointer part of the lambda's state. Take the object as a parameter:

std::function<void(Class_A&)> getFunctionToRunLater()
{
    return [] (Class_A& obj) { obj.somethingToDo(); obj.moreThingsToDo(); };
}
like image 160
TheOperator Avatar answered Sep 24 '22 13:09

TheOperator