Home > Blockchain >  Initialization order of static inline member variables in class templates (C 17)
Initialization order of static inline member variables in class templates (C 17)

Time:10-06

I am working on a code where I need a static member variable of some class to be initialized using a static variable of an instance of a class template. I know about the static initialization order fiasco and found several discussions on that issue but none of them really helped me with my problem. I don't even know why that should be a problem in my case.

This is a minimal example that reproduces the error I get from my code:

#include <string>
#include <map>


template<class T>
class Foo {

private:

    static inline std::map<std::string, T> map_ = {};
    
public:
    
    static bool insert(const std::string& key, T value) {

        map_[key] = value;
        return true;
    }
};


using MyFoo = Foo<char>;

class Bar {
    static inline bool baz_ = MyFoo::insert("baz", 'A');
};


int main() {
    // This works just fine if the definition of Bar::baz_ in line 24 is removed
    //MyFoo::insert("baz", 'A');
    return 0;
}

Compilation using the C 17 standard finishes with 0 warnings and 0 errors. When the program is executed, however, a segmentation fault occurs when Foo::insert is called. It appears that Foo::map_ is not initialized at that point. But shouldn't the static variables be initialized in the same order as they are defined in the code?

I should also mention that the code without the template works fine. So I am wondering if the compiler instantiates the template in a way that the the actual class is defined after Bar. Could something like that be the problem or does the compiler just happen to do the 'right' thing in that case?

CodePudding user response:

Dynamic initialization of static data members of class template specializations (if they are not explicitly specialized) are completely unordered (indeterminately-sequenced) with any other dynamic initialization. It doesn't matter whether the member is inline or not, it doesn't matter whether the other dynamic initialization is also of a static data member of a class template specialization or not, and it also doesn't matter where the points of instantiation and definition of the static data member are located in the translation unit.

Therefore there is no way to guarantee that Foo<char>::map_ is initialized before Bar::baz_.

Instead use the common idiom of using a static member function containing the static member as a local static variable instead:

static auto& map_() {
    static std::map<std::string, T> map = {};
    return map;
}

// replace `map_` with `map_()` everywhere else
  • Related