Static Initialization Order for Singletons

494 views Asked by At

So I'm reading that for Zero Initialization will initialize:

Every named variable with static or thread-local storage duration that is not subject to constant initialization, before any other initialization

I am using a Singleton with the traditional private constructor and a static public method in which there is a local static singleton object which the method will be return.

My problem is that the class also has a static vector which is zero-initialized and it seems to be getting initialized after the singleton, meaning that I cannot interact with it. Is there something that governs this initialization order or is that simply implementation defined?


This is a simplification of what my code looks like:

class Foo {
    Foo(){ s_vec.push_back(13); }
public:
    static Foo& Get() {
        static Foo singleton;
        return singleton;
    }

    int Front() const { return s_vec.front(); }
    static vector<int> s_vec;
};
vector<int> Foo::s_vec;

I'm running into this problem because elsewhere in the code I'm initializing a static global variable like this and not getting 13: static const auto element = Foo.Get().Front()

1

There are 1 answers

1
Philipp Claßen On

Constructor for global variables are executed before the start of main, but the order across compilation units is not specified.

The Foo constructor in your example should only be called once you call Foo::Get. If the first time you call it is in main, the static vector will be already initialized.

One situation, where you can run into the race that you describe is when you call Foo::Get in the initialization code of another global object, especially when the code is in another compilation unit.

But in a simple test like this here, the vector should always be initialized first and there will be no possible race:

class Foo {
    Foo() = default;
public:
    static Foo& Get() {
        static Foo singleton;
        return singleton;
    }
    static vector<int> s_vec;
};
vector<int> Foo::s_vec; // will be initialized before main

int main() {
  Foo::Get(); // --> triggers constructor call Foo::Foo
  return 0;
}

(I'm assuming Foo::Get is a static member in the singleton, otherwise you cannot instantiate it. But it does not make a difference conceptually.)

The problematic scenario could look like this:

// other file
struct Bar {
  Bar() { Foo::Get(); }
};
Bar bar; // global object

You have no control about the initialization order of Foo::s_vec (in the first compilation unit) and bar (in the second one).