Так что я читаю это для Нулевая инициализация будет инициализировать:
Каждая именованная переменная со статическим или локальным потоком хранения, которая не подлежит постоянной инициализации, перед любой другой инициализацией
Я использую одиночка с традиционным приватным конструктором и статическим публичным методом, в котором есть локальный статический одноэлементный объект, который метод будет возвращать.
Моя проблема в том, что класс также имеет статический vector
который инициализируется нулем, и, кажется, инициализируется после синглтон, что означает, что я не могу взаимодействовать с ним. Есть ли что-то, что управляет этим порядком инициализации, или это просто определяется реализацией?
Это упрощение того, как выглядит мой код:
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;
Я сталкиваюсь с этой проблемой, потому что в другом месте кода я инициализирую статическую глобальную переменную, подобную этой, и не получаю 13: static const auto element = Foo.Get().Front()
Конструктор для глобальных переменных выполняется перед началом main, но порядок между единицами компиляции не указан.
Foo
Конструктор в вашем примере должен вызываться только после вызова Foo::Get
, Если в первый раз вы звоните, это в main
статический вектор будет уже инициализирован.
Одна ситуация, когда вы можете столкнуться с гонкой, которую вы описываете, это когда вы звоните Foo::Get
в коде инициализации другого глобального объекта, особенно когда код находится в другом модуле компиляции.
Но в таком простом тесте, как этот, вектор всегда должен сначала инициализироваться, и возможной гонки не будет:
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;
}
(Я предполагаю, что Foo::Get
является статическим членом в синглтоне, иначе вы не можете создать его экземпляр. Но это не имеет значения концептуально.)
Проблемный сценарий может выглядеть так:
// other file
struct Bar {
Bar() { Foo::Get(); }
};
Bar bar; // global object
Вы не можете контролировать порядок инициализации Foo::s_vec
(в первом блоке компиляции) и bar
(во втором).
Других решений пока нет …