When can auto
be used as the type specifier of a variable initialized with a lambda function? I'm try to use auto
in the following program:
#include <iostream>
#include <functional>
class A
{
const std::function <void ()>* m_Lambda = nullptr;
public:
A(const std::function <void ()>& lambda): m_Lambda (&lambda) {}
void ExecuteLambda()
{
(*m_Lambda)();
}
};
void main()
{
int i1 = 1;
int i2 = 2;
const auto lambda = [&]()
{
std::cout << "i1 == " << i1 << std::endl;
std::cout << "i2 == " << i2 << std::endl;
};
A a(lambda);
a.ExecuteLambda();
}
I'm using Visual Studio Community 2019 and when I start executing a.ExecuteLambda()
, the program stops with the following exception:
Unhandled exception at 0x76D9B5B2 in lambda.exe:
Microsoft C exception: std :: bad_function_call at memory location 0x00B5F434.
If I change the line const auto lambda = [&]()
to const std::function <void ()> lambda = [&]()
, it works perfectly. Why is it not allowed to use auto
? Can something be changed to allow it to be used?
CodePudding user response:
A lambda expression does not result in a std::function
. Instead, it creates an unnamed unique class type and that has an overload for operator()
. When you pass your lambda to A
's constructor, it creates a temporary std::function
object, and you store a pointer to that temporary object. When A
's constructor ends, that temporary object is destroyed, leaving you with a dangling pointer.
To fix this, just get rid of using pointers. That would look like
#include <iostream>
#include <functional>
class A
{
std::function <void ()> m_Lambda;
public:
A(const std::function <void ()> lambda): m_Lambda (lambda) {}
void ExecuteLambda()
{
m_Lambda();
}
};
void main()
{
int i1 = 1;
int i2 = 2;
const auto lambda = [&]()
{
std::cout << "i1 == " << i1 << std::endl;
std::cout << "i2 == " << i2 << std::endl;
};
A a(lambda);
a.ExecuteLambda();
}
CodePudding user response:
You are storing a dangling std::function
pointer in your A
object.
A lambda expression is not a std::function
object, it is a compiler-defined type that is assignable to a std::function
object.
When you declare lambda
using auto
, it gets a unique type. To bind this lambda
to the A
constructor's parameter, a temporary std::function
object is created, which you are storing a pointer to. But then, that temporary gets destroyed when the constructor exits, which is why you get the exception when you try to execute the std::function
.
When you change the declaration of lambda
to std::function
instead, the A
constructor's parameter is able to bind to that object as-is, and no temporary is created.
You should be passing and storing std::function
objects by value instead of by pointer, eg:
#include <iostream>
#include <functional>
class A
{
std::function<void()> m_Lambda;
public:
A(std::function<void()> lambda): m_Lambda(lambda) {}
void ExecuteLambda()
{
m_Lambda();
}
};
int main()
{
int i1 = 1;
int i2 = 2;
const auto lambda = [&]()
{
std::cout << "i1 == " << i1 << std::endl;
std::cout << "i2 == " << i2 << std::endl;
};
A a(lambda);
a.ExecuteLambda();
return 0;
}