Reputation: 26335
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();
}
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
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
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
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
Reputation: 58848
Static local variables are initialized the first time their declaration is executed.
In this case, you:
print_func
print_func
)Captured values are always captured when the lambda is created - which in this case is during the first call to call_print
.
Upvotes: 8
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
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