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

Access to std::unordered_map item crashes when key is base class pointer involved in virtual inheritance

以下代码适用于 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;
}

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

第二个问题在技术上是 UB 并且不应该在 lambda 中工作(不过,RVO 使它可行)你通过引用捕获 o 并且一旦你离开函数 f1().由于 RVO,它可能仍然存在,但这仍然被认为是 UB。

编辑:离开范围后捕获变量的 lambda 会发生什么情况?与无捕获的 lambda 不同,它不应该是可访问的。

C2* f1()
{
    C2* o = new C2;
    o->f = [&]()
    {
        std::cout << uMap[o] << '\n'; // MSVC: 0xC0000005: Access violation reading location 
    };
    return o;
}

此代码的问题在于 lambda 通过引用捕获局部变量 o。当函数 f1() returns 的范围 o 不再存在。所以你有一个悬而未决的参考!因此,当您调用 lambda 时,您会得到未定义的行为。两个版本的地图都会出现这种情况。

要解决此问题,您可以改为按值捕获:

o->f = [=]()
{
    std::cout << uMap[o] << '\n'; 
};

这里的指针被lambda复制,在函数returns后生效