当键是虚拟继承中涉及的基类指针时,对std :: unordered_map项的访问会崩溃

问题描述 投票:2回答:2

以下内容适用于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;
}
c++ unordered-map virtual-inheritance
2个回答
3
投票
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复制的,在函数返回后它将有效。


1
投票

这不应该编译,因为std::function不能假定lambda捕获变量。不知道为什么编译。

从技术上来说,它是UB,在lambda中不起作用(因此,RVO使它可行)的第二个问题,是通过引用捕获o的,并且一旦离开函数f1(),该引用在技术上就无效了。由于RVO,它可能仍然存在,但仍被认为是UB。

编辑:离开范围后,捕获变量的lambda也会发生什么?它不应该像无法捕获的lambda一样不可访问。

© www.soinside.com 2019 - 2024. All rights reserved.