In the code snippet below, the assertion in foo
always fires.
Can anyone explain why y
is a nullptr
? It looks like a lifetime issue, i.e. y
is destroyed between the calls to put
and get
but I don't really understand why.
What am I missing?
TIA
class Y
{
public:
Y(const std::string& name) : m_name(name)
{
}
const std::string& getName() const
{
return m_name;
}
private:
Y(const Y&);
const std::string m_name;
};
void foo(std::unique_ptr<Y> y)
{
if (y == nullptr)
{
// ALWAYS FIRES
assert(false && "nullptr\n");
}
std::string name = y->getName();
std::cout << "name: " << name << "\n";
}
class X
{
public:
X() {}
void put(std::unique_ptr<Y> y)
{
m_queue.push([&] { foo(std::move(y)); });
}
void get()
{
std::function<void()> func = m_queue.front();
func();
}
private:
std::queue<std::function<void()>> m_queue;
};
int main()
{
std::unique_ptr<Y> y(new Y("fred"));
X x;
x.put(std::move(y));
x.get();
return 0;
}
void put(std::unique_ptr<Y> y)
{
m_queue.push([&] { foo(std::move(y)); });
}
Here, you push a lambda that contains a reference to the local variable y
. The moment you leave put
, the local variable is destroyed, and the lambda contains a dangling reference. Any further behavior is undefined.
You would need to capture the local variable by moving it into the lambda, but that is quite advanced, and also insufficient, because std::function
cannot hold move-only function objects. The easiest way to solve this is to switch from unique_ptr
to shared_ptr
and capturing by value in the lambda.