c++stdmapobject-construction

Why does std::map crash when it is declared static inline inside a class and used early?


I have found several times that when a std::map is declared inside a class as a static inline (C++ 17),

struct MyStruct
{
    static inline std::map <A, B> mymap;

    MyStruct(A& a, B& b)
    {
        mymap[a] = b;
    }
};

the MyStruct constructor will crash if it is called early, i.e. before main, inside the first use of the map member.

If the std::map is declared a different way, i.e.,

struct MyStruct
{
    static std::map <A, B>& mymap()
    {
        static std::map <A, B> map;
        return map;
    }
    
    MyStruct(A& a, B& b)
    {
        mymap()[a] = b;
    }
};

then no crash happens.

I would have thought that in both cases the map would be initialized before the call to MyStruct constructor would be allowed to proceed.

Can anyone explain what is happening here?


Solution

  • Declaring a static inline class member effectively ODR-defines that class member in some randomly-chosen translation unit that forms the final executable.

    At this point Static Initialization Order Fiasco becomes a factor when the class member gets referenced in a translation unit in the manner you described.

    This results in undefined behavior.

    The function-scoped static class instance is a well known technique for defeating the static initialization order fiasco. Function-scoped static objects have well-defined initialization semantics: the first time the function gets called, from any translation unit.