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:
f5
), ornullptr
(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.
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.
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.
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 }
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With