Я знаю о статический порядок инициализации фиаско в C ++ и
построить при первом использовании идиома чтобы избежать этого. Таким образом, в коде ниже
глобальное назначение a
может случиться до того foo::a
так что
значение a
не определено С другой стороны, глобальный
назначение b
все в порядке, так как он вызвал функцию foo::b()
,
#include <iostream>
#include <string>
using namespace std;
// foo.hpp
class foo {
public:
static const string a;
static const string& b();
static const char* const c;
static const char* const d[2];
static const int e;
static const int f[2];
};
// foo.cpp
const string foo::a("astr");
const string& foo::b() {
static const string t("bstr");
return t;
}
const char* const foo::c = "cstr";
const char* const foo::d[2] = {"dstr1", "dstr2"};
const int foo::e = 5;
const int foo::f[2] = {6, 7};
// main.cpp
// global initializations
string a = foo::a; // dangerous, might be "" or "astr"string b = foo::b(); // safe, guaranteed to be "bstr"const char* c = foo::c; // what about these...?
const char* d = foo::d[0];
int e = foo::e;
int f = foo::f[0];
int main() {
cout << a << " " << b << "\n"<< c << " " << d << "\n"<< e << " " << f << "\n";
}
(Представь, что я объединил foo.hpp
, foo.cpp
, а также main.cpp
Вот.)
Однако как насчет переменных, которые являются встроенными типами или массивами из них?
Таким образом, глобальные назначения c
, d
, e
, а также f
безопасно в этом коде?
Кажется возможным, что компоновщик может установить память для этих переменных
поэтому во время выполнения инициализация не требуется. Но могу ли я положиться на
этот?
Я знаю, что я не должен использовать глобальные переменные. Тем не менее, я автор
библиотеки (foo.cpp и foo.hpp), и я не имею никакого контроля над тем, что
пользователь моей библиотеки (автор main.cpp) делает.
Ключевым моментом здесь является разница междуstatic
«инициализация» (формально известный с использованием языка Стандарта как динамическая инициализация объектов со статической продолжительностью хранения который имеет фиаско заказа) и статическая инициализация.
Стандарт говорит (раздел [basic.start.static]
) тот
Постоянный инициализатор для объекта
o
является выражением, которое является константным выражением, за исключением того, что оно также может вызыватьconstexpr
конструкторы дляo
и его подобъекты, даже если эти объекты не литеральных типов классов. [Примечание: у такого класса может быть нетривиальный деструктор — примечание конца]Постоянная инициализация выполняется:
- если каждое полное выражение (включая неявные преобразования), которое появляется в инициализаторе ссылки со статической или потоковой продолжительностью хранения, является константным выражением, и ссылка связана с glvalue, обозначающим объект со статической длительностью хранения, с временным объектом или подобъектом из этого или для функции;
- если объект со статической или потоковой длительностью хранения инициализируется вызовом конструктора, и если полное выражение инициализации является постоянным инициализатором для объекта;
- если объект со статической или потоковой длительностью хранения не инициализируется вызовом конструктора, и если либо объект инициализируется значением, либо каждое полное выражение, которое появляется в его инициализаторе, является константным выражением.
Если постоянная инициализация не выполняется, переменная со статической продолжительностью хранения или продолжительностью хранения потока инициализируется нулями. Вместе нулевая инициализация и постоянная инициализация называются статической инициализацией; все остальные инициализации — это динамическая инициализация. Статическая инициализация должна быть выполнена до любой динамической инициализации.
Ваш c
, d
, e
, а также f
объекты имеют постоянные инициализаторы, поэтому их инициализация завершается на этапе статической инициализации (даже если c
а также d
сами по себе НЕ являются постоянными), и их значения доступны во время всей динамической инициализации, даже те, которые были лексически раньше.
Других решений пока нет …