You're not storing a lambda, you're storing a reference to a std::function
.
In fact, that std::function
is being created as a temporary when the lambda is implicitly converted to std::function
. That std::function
temporary dies after the line where the constructor is invoked.
LambdaStore(const std::function<void(float)>& _fn) // _fn refers to a temporary
: fn(_fn)
{
fn(11.1f); // works
} // fn (and _fn) dies
But, even if you would change your class to use the lambda type directly through a template, the lambda itself would die too, but this is true with any C++ type, no matter the type. Consider with int
s:
class LambdaStore
{
public:
LambdaStore(const int& _i)
: i(_i)
{
std::cout << i; // works
}
void ExecuteStoredLambda()
{
std::cout << i; // crashes
}
private:
const int& i;
};
void main()
{
LambdaStore lambdaStore(1); // temporary int created here
// temporary int gone
lambdaStore.ExecuteStoredLambda();
}
Temporaries don't get lifetime extension past the statement they are created for when they are bound to a function parameter.
They do get lifetime extension if it binds directly to a member reference, when using the braces only, though:
struct ref {
const int& i
};
int main() {
ref a{3};
std::cout << a.i; // works
ref b(3);
std::cout << b.i; // crashes
}
The solution is obviously to store the std::function
by value instead of by reference:
class LambdaStore
{
public:
LambdaStore(const std::function<void(float)>& _fn)
: fn(_fn)
{
fn(11.1f); // works
}
void ExecuteStoredLambda()
{
fn(99.9f); // will also work
}
private:
std::function<void(float)> fn; // no & here
};
g++ -fanalyzer
misses it, too. – Mudlark