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

2

The following works with g++, but crashes when compiled with MSVC. I don't know if my code has undefined behaviour or whatever. Minimal example:

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
asked on Stack Overflow Dec 22, 2019 by Powereleven • edited Dec 22, 2019 by Powereleven

2 Answers

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

The problem with this code is that the lambda is capturing a local variable o by reference. When the function f1() returns the scope of o no longer exists. So you have a dangling reference! Therefore, when you invoke the lambda you get undefined behaviour. This happens with both versions of the map.

To get around the problem you can capture by value instead:

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

Here the pointer is copied by the lambda and it will be valid after the function returns.

answered on Stack Overflow Dec 22, 2019 by jignatius • edited Mar 31, 2021 by jignatius
1

This shouldn't compile as std::function cannot assume lambdas that capture variables. Not sure why it compiles.

Second issue that is technically UB and shouldn't work (tho, RVO makes it workable) in lambda you capture o by reference and that reference is technically invalidated once you leave the function f1(). Due to RVO it might still remain but this is still considered UB.

Edit: also what happens to the lambda that captures variables after you leave the scope? It shouldn't be accessible unlike captureless lambdas.

answered on Stack Overflow Dec 22, 2019 by ALX23z • edited Dec 22, 2019 by ALX23z

User contributions licensed under CC BY-SA 3.0