Подтвердить что ты не робот

Когда инициализация глобальных констант с внешней связью безопасна от фиаско порядка статической инициализации?

Рассмотрим следующий пример:

  • tt.h объявляет глобальную константу с внешними связями extern int g_TRAGIC;

  • tt.cpp определяет g_TRAGIC следующим образом const int g_TRAGIC = 0xF001;

  • my.cpp хочет использовать его для определения своей собственной глобальной константы const int g_MAGIC = g_TRAGIC;

Когда я прочитал iso-FAQ, я бы предположил, что это приводит к фиаско порядка статической инициализации. Однако примечания ISO-FAQ

Фиксирование порядка статической инициализации также может быть в некоторых случаях применимо к встроенным/внутренним типам.

Что означают эти некоторые случаи? В каких условиях мы сохраняем и воспроизводим SIOF для встроенных/внутренних типов, в частности констант? Или необходимо, чтобы Construct On First Use Idiom использовался для констант all с внешней связью?

Примечание: в реальном коде я не могу изменить определение g_TRAGIC.

4b9b3361

Ответ 1

Компиляторы могут создавать различные типы кода.

Статический инициализированный сегмент данных

Компилятор передает в раздел данных имя и его начальное значение.

.data
   dw myData   6

Это инициализируется во время компиляции и безопасно определяется на протяжении всего срока службы программы.

построенные данные

Другая альтернатива заключается в том, что компилятор резервирует некоторое пространство для этой переменной и создает инициализатор/конструктор для данных, а затем вызывает конструктор непосредственно перед main. При выполнении деструктора (если требуется) atexit.

 class CriticalSection {
      CRITICAL_SECTION m_myCS;
      public:
         CriticalSection() {
              InitializeCriticalSection( &m_myCS );
         }
         ~CriticalSection() {
              DeleteCriticalSection( & m_myCS );
         }
 } cs;

в сочетании

Некоторые данные могут выполняться на обоих этапах.

 struct Data {
     bool initialized;
     void *(*pMalloc)( size_t size );
 }  FixMalloc = { true, MyMalloc };

Я видел, как компиляторы (VS2013) генерируют код, который инициализирует initialized значение true в статических данных, но создает функцию для назначения pMalloc MyMalloc во время выполнения. (Это объясняется тем, что для MyMalloc не была известна константа.)

метод singleton

 SomeClass * GetSomeClass()
 {
     static SomeClass cls;
     return &cls;
 }

Этот порядок определен - когда он вызывается, но требует, чтобы компилятор полностью C++11 был потокобезопасным.

Резюме

Гарантии: -

  • статики в одном модуле компиляции инициализируются сверху вниз.
  • статика инициализируется однопоточной.
  • синглтоны определили порядок построения. Но не обязательно безопасный поток.

Гарантии не являются: -

  • Все объекты инициализируются одновременно.
  • Статическая инициализация имеет рабочую среду выполнения.

Перед вызовом main, ваша статика и среда выполнения C/С++ являются загрузочными. Кроме того, порядок создания проблемы возникает между вашим кодом и временем выполнения, поэтому вы можете создавать некоторые элементы со сложными конструкторами, которые полагаются на службы, которые не могут быть доступны.

Ответ 2

Дальнейшее чтение iso-FAQ дает нам ответ.

SIOF происходит, если вы пытаетесь инициализировать встроенный/встроенный тип возвращаемым значением постоянной функции.

const int g_MAGIC = f(g_TRAGIC);