void.pointer
void.pointer

Reputation: 26335

When is 'this' captured in a lambda?

I have a function in a class that defines a lambda and stores it in a local static variable:

class A
{
public:
    void call_print()
    {
        static auto const print_func = [this] {
            print();
        };

        print_func();
    };

    virtual void print()
    {
        std::cout << "A::print()\n";
    }
};

class B : public A
{
public:
    virtual void print() override
    {
        std::cout << "B::print()\n";
    }
};

I also execute the following test:

int main()
{
    A a;
    B b;

    a.call_print();
    b.call_print();
}

(Live Sample)

What I expect to be printed is:

A::print()
B::print()

But what I really get is:

A::print()
A::print()

(Same object address is also printed with each)

I suspect this is due to the this capture. I assumed that it would capture the value of this when it is called, however it seems to be captured the moment the lambda is defined.

Could someone explain the semantics of lambda captures? When do they actually get provided to the function? Is it the same for all capture types, or is this a special case? Removing static fixes the problem, however in my production code I'm actually storing the lambda in a slightly-heavier object which represents a slot to which I insert into a signal later.

Upvotes: 24

Views: 3494

Answers (6)

Simon Kraemer
Simon Kraemer

Reputation: 5680

I don't think the capture is the problem here but the static keyword. Think about your code as this:

class A
{
public:
    void call_print()
    {
        static A* ptr = this;

        ptr->print();
    };

    virtual void print()
    {
        std::cout << "A::print()\n";
    }
};

class B : public A
{
public:
    virtual void print() override
    {
        std::cout << "B::print()\n";
    }
};

This is pretty much the same without a lambda.

If you look at the code it becomes pretty clear that your call a.call_print(); initializes ptr with a pointer to the object a which is then used further on.

Upvotes: 4

Nicol Bolas
Nicol Bolas

Reputation: 473272

This has nothing to do with the semantics of lambda capture. It's simply how static works.

A static function-scoped variable is initialized exactly once. There is only ever one such object in your entire program. It will be initialized the first time the function is called (more specifically, the first time the static statement is executed). And therefore, the expression used to initialize the static variable is only ever invoked once.

So if a static function-scoped variable is initialized with data that's based on one of the function's parameters (like this), then it will only get the parameters from the first invocation of that function.

Your code creates a single lambda. It does not create different lambdas on each invocation of the function.

The behavior you seem to want is not a function-local static variable, but an object member. So just put a std::function object in the class itself, and have call_print initialize it if it is empty.

Upvotes: 36

Richard Hodges
Richard Hodges

Reputation: 69864

This question is not so much about the behaviour of lambdas, but about the behaviour of static variables in functions.

The variable is created the first time code flows over it, using whatever variables are available at that time.

example:

#include <iostream>

int foo(int v)
{
  static int value = v;
  return v;
};

int main()
{
  std::cout << foo(10) << std::endl;
  std::cout << foo(11) << std::endl;
}

expected:

10
10

Because it's equivalent to:

foo::value = 10;
std::cout << foo::value << std::endl;
// foo::value = 11; (does not happen)
std::cout << foo::value << std::endl;

Upvotes: 3

Static local variables are initialized the first time their declaration is executed.

In this case, you:

  1. Create a lambda, capturing &A as this, that calls A.print();
  2. Assign that lambda to print_func
  3. Call that lambda (through print_func)
  4. Call that lambda again.

Captured values are always captured when the lambda is created - which in this case is during the first call to call_print.

Upvotes: 8

juanchopanza
juanchopanza

Reputation: 227390

The lambda is instantiated the first time the enclosing function A::call_print() is called. Since the first time you call it on an A object, the this of that object is captured. If you reversed the order of invocation, you'd see a different result:

b.call_print();
a.call_print();

Output:

B::print()
B::print()

It more to do with the semantics of initialization of function-local static objects than those of lambda capture.

Upvotes: 12

Smeeheey
Smeeheey

Reputation: 10316

Indeed, the value of the capture is set when the lambda is defined, not when its called. Because you're setting a static variable to the expression that defines the lambda, this only happens the very first time the function call_print is called (by the rules governing static variables). Thus all call_print calls thereafter in fact get the same lambda, the one whose this is set to &a.

Upvotes: 2

Related Questions