В C++ простые фиктивные инициализации встроенного типа неуязвимы для статического порядка инициализации?

Я знаю о статическом порядке инициализации фиаско в 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).

1 ответ

Решение

Ключевым моментом здесь является разница между static "инициализация" (формально известный с использованием языка Стандарта в качестве динамической инициализации объектов со статической продолжительностью хранения, которая имеет фиаско порядка) и статическая инициализация.

Стандарт говорит (раздел [basic.start.static]) тот

Постоянный инициализатор для объекта o является выражением, которое является константным выражением, за исключением того, что оно также может вызывать constexpr конструкторы для o и его подобъекты, даже если эти объекты не литеральных типов классов. [Примечание: у такого класса может быть нетривиальный деструктор - примечание конца]

Постоянная инициализация выполняется:

  • если каждое полное выражение (включая неявные преобразования), которое появляется в инициализаторе ссылки со статической или потоковой продолжительностью хранения, является константным выражением, и ссылка связана с glvalue, обозначающим объект со статической длительностью хранения, с временным объектом или подобъектом из этого или для функции;
  • если объект со статической или потоковой длительностью хранения инициализируется вызовом конструктора, и если полное выражение инициализации является постоянным инициализатором для объекта;
  • если объект со статической или потоковой длительностью хранения не инициализируется вызовом конструктора, и если либо объект инициализируется значением, либо каждое полное выражение, которое появляется в его инициализаторе, является константным выражением.

Если постоянная инициализация не выполняется, переменная со статической продолжительностью хранения или продолжительностью хранения потока инициализируется нулями. Вместе нулевая инициализация и постоянная инициализация называются статической инициализацией; все остальные инициализации - это динамическая инициализация. Статическая инициализация должна быть выполнена до любой динамической инициализации.

Ваш c, d, e, а также f объекты имеют постоянные инициализаторы, поэтому их инициализация завершается на этапе статической инициализации (даже если c а также d сами по себе НЕ являются постоянными), и их значения доступны во время всей динамической инициализации, даже те, которые были лексически раньше.

Другие вопросы по тегам