Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lambda expression sneakily capturing `this`

Tags:

c++

c++11

lambda

Suppose I have the following.

struct A
{
    int x;
    std::function<int()> f1() { return [=](){ return x; }; }
    std::function<int()> f2() { return [=](){ return this->x; }; }
    std::function<int()> f3() { return [this](){ return x; }; }
    std::function<int()> f4() { return [this](){ return this->x; }; }
    std::function<int()> f5()
    {
        int temp = x;
        return [=](){ return temp; };
    }
}

And now I have the following code.

auto a = std::make_shared<A>();
a->x = 5;
std::function<int()> f = a.f#();
a.reset();
int x = f();

where the f# is referring to any of f1, f2, f3, f4, f5.

These functions are exhibiting behavior in one of two sets:

  1. return 5 when called (f5), or
  2. crashed trying to dereference nullptr (f1, f2, f3, f4).

I understand that this is because some are capturing "this" in the member function of A, either implicitly or explicitly.

What is the formal rule that's determining behavior 1 or 2?

I spent a while dealing with a bug that was caused by something similar to f1, thinking it would capture x and never considering it would capture this, so I figured it would be useful to get this documented.

like image 996
Timothy Shields Avatar asked Mar 25 '13 04:03

Timothy Shields


People also ask

What does it mean to lambda capture this?

A lambda is a syntax for creating a class. Capturing a variable means that variable is passed to the constructor for that class. A lambda can specify whether it's passed by reference or by value.

What does [=] mean in lambda function?

You can use a capture-default mode to indicate how to capture any outside variables referenced in the lambda body: [&] means all variables that you refer to are captured by reference, and [=] means they're captured by value.

What are lambda expressions in C++?

C++ 11 introduced lambda expressions to allow inline functions which can be used for short snippets of code that are not going to be reused and therefore do not require a name. In their simplest form a lambda expression can be defined as follows: [ capture clause ] (parameters) -> return-type { definition of method }

Are functors the same as lambdas?

Lambdas are just regular C++ classes. They make use of the operator() member function. These are actually called “functors” in C++ There is no extra overhead or performance penalty for using them.


1 Answers

There is no formal rule that determines this behavior. Because this behavior is undefined.

Your lambdas are accessing an object that doesn't exist. You can't capture a member variable directly by value; you always capture them by this. Which means you're capturing them by reference. Once the object is deleted, any attempt to access that deleted object results in undefined behavior.

The exception to this is f5, which should return a consistent value, guaranteed. It's completely disconnected from the originating object.

like image 157
Nicol Bolas Avatar answered Oct 09 '22 10:10

Nicol Bolas