以下内容适用于g ++,但使用MSVC编译时会崩溃。我不知道我的代码是否具有未定义的行为或其他任何行为。最小示例:
class C1
{
};
// without virtual, it works.
// I need virtual because there is a C3 that inherits from C1,
// and then C4 that inherits from C2 and C3
class C2 : virtual public C1
{
public:
std::function<void()> f;
};
std::unordered_map<C1*, int> uMap;
//std::unordered_map<C2*, int> uMap; // doesn't crash
C2* f1()
{
C2* o = new C2;
o->f = [&]()
{
std::cout << uMap[o] << '\n'; // MSVC: 0xC0000005: Access violation reading location
};
return o;
}
int main()
{
auto o = f1();
o->f();
delete o;
}
C2* f1() { C2* o = new C2; o->f = [&]() { std::cout << uMap[o] << '\n'; // MSVC: 0xC0000005: Access violation reading location }; return o; }
此代码的问题在于,lambda正在通过引用捕获局部变量o
。当函数f1()
返回时,o
的范围不再存在。因此,您可以引用一个已不存在的变量!因此,当您调用lambda时,您将获得不确定的行为。两种版本的地图都会发生这种情况。
要解决此问题,您可以按值捕获:
o->f = [=]()
{
std::cout << uMap[o] << '\n';
};
这里指针是由lambda复制的,在函数返回后它将有效。
这不应该编译,因为std::function
不能假定lambda捕获变量。不知道为什么编译。
从技术上来说,它是UB,在lambda中不起作用(因此,RVO使它可行)的第二个问题,是通过引用捕获o
的,并且一旦离开函数f1()
,该引用在技术上就无效了。由于RVO,它可能仍然存在,但仍被认为是UB。
编辑:离开范围后,捕获变量的lambda也会发生什么?它不应该像无法捕获的lambda一样不可访问。