我有一个有这样一个公共std::function
成员的课程:
class B
{
public:
B(std::function<void(void)> _func = NULL) : m_function(_func) { }
std::function<void()> m_function;
};
我有一个成员函数X
的课程SomeFunction
:
class X
{
public:
void SomeFunction(const std::string & _s)
{
//...
}
};
我还有一个类A
,它返回B
类型的对象,如下所示:
class A
{
private:
X x;
public:
B GetObjectB()
{
std::string local = "abc";
return B(([&]() -> void { x->SomeFunction(local); }))
}
};
然后我执行
A a;
B b = a.GetObjectB();
b.m_function();
代码正常工作,直到调用函数SomeFunction
,并且输入参数_s
没有值,尽管已将local
作为其输入参数。
我在这里做错了什么?
答案 0 :(得分:3)
你告诉你的lambda默认通过引用捕获([&]
),它正在捕获堆栈上local
的引用。作为堆栈变量,当范围退出时,这将被释放,因此返回的对象的my_function
已捕获了悬空引用。试试[=]
。
答案 1 :(得分:3)
这段代码
std::string local="abc";
return B(([&]()->void{x->SomeFunction(local);}))
通过引用捕获所有使用的变量,并返回该函数。捕获的变量中有局部变量local
,其范围在范围退出时结束,即从函数返回时。因此,当稍后执行lambda时, local 变量不再存在,这意味着捕获的引用无效(“悬空引用”)。
引自http://en.cppreference.com/w/cpp/language/lambda:
悬空参考
如果通过引用隐式或显式捕获实体,并且 在调用之后调用闭包对象的函数调用操作符 实体的生命周期已经结束,发生了未定义的行为。 C ++ 闭包不会延长捕获的引用的生命周期。
本段指出在其他支持闭包的语言中(例如在JavaScript中),通过引用捕获的变量的生命周期是扩展,而在C ++中并非如此。
变量x
存在同样的问题,它是调用函数的类A
的成员变量。虽然您的测试中可能没有有问题的代码,但这也可能成为一个问题:
B b;
// some other context
{
A a;
b = a.GetObjectB();
}
// The lambda previously returned from a.GetObjectB() is now
// stored in b. But it still refers to the now dead a.x!
b.m_function(); // BOOM!
一个简单的解决方法是简单地不通过引用捕获所有变量,而是按值捕获,因此变量将被复制到lambda实例中:
std::string local="abc";
return B(([=]()->void{x->SomeFunction(local);}))
当您不想复制值时,可能的解决方法在我对一个非常相似的问题的回答中进行了讨论:https://stackoverflow.com/a/20928847/592323。