Рассмотрим следующий код, разбитый на три модуля компиляции:
a.h
:
struct A
{
void Register(const char* s);
const char* m_s[10];
int m_i = 0;
};
A& GetA();
a.cpp
:
#include "a.h"#include <stdio.h>
void A::Register(const char* s)
{
m_s[m_i++] = s;
}
A& GetA()
{
static A instance;
return instance;
}
int main(int argc, char* argv[])
{
A& a = GetA();
int n = a.m_i;
for (int i = 0; i < n ; ++i)
printf("%s\n", a.m_s[i]);
return 0;
}
b.cpp
:
#include "a.h"struct B
{
B() { GetA().Register("b"); }
static B* instance;
};
B* B::instance = new B;
c.cpp
:
#include "a.h"struct C
{
C() { GetA().Register("c"); }
static C* instance;
};
C* C::instance = new C;
Код строится и работает нормально, используя gcc (-std = c ++ 11), создавая вывод:
c
b
Теперь, ссылаясь на cppreference.com:
Отложенная динамическая инициализация
Определяется ли реализация динамической инициализацией
происходит — перед первым оператором главной функции (для статики)
или начальная функция потока (для локальных потоков), или отложенная
случиться после.Если инициализация не встроенной переменной
отложено после первого оператора функции main / thread,
это происходит до первого использования odr любой переменной со static / thread
продолжительность хранения, определенная в той же единице перевода, что и переменная
быть инициализированным. Если ни одна переменная или функция не используется odr из данного
единица перевода, нелокальные переменные, определенные в этом переводе
блок никогда не может быть инициализирован (это моделирует поведение
динамическая библиотека по запросу). Тем не менее, пока что-нибудь из TU
используется odr, все нелокальные переменные, чья инициализация или уничтожение
побочные эффекты будут инициализированы, даже если они не используются в
программа.
Обратите внимание, что a.cpp
не знает о существовании B
а также C
и что единственные взаимодействия B
& C
с A
являются вызовами GetA()
а также A::Register()
во время строительства их соответствующих экземпляров.
Насколько я вижу, B
& C
случаи не используются ODR, и, конечно, не из main()
переводческий блок. Их инициализация явно имеет побочные эффекты, но мне кажется, что нет никакой гарантии, что эта инициализация произойдет до входа в main()
или раньше main()
печатает зарегистрированные строки — или даже вообще.
Итак — наконец — мой вопрос это: Тот факт, что B
а также C
экземпляры инициализируются раньше main()
печатает зарегистрированные строки из-за не к стандарту, но вместо этого к поведению, определенному в gcc?
Если это гарантировано стандартом, то как?
Является ли тот факт, что
B
а такжеC
экземпляры инициализируются раньшеmain()
печатает зарегистрированные строки не из-за стандарта, а из-за поведения, определенного в gcc?
это не гарантировано стандартом. Наиболее актуальная часть цитаты:
Если ни одна переменная или функция не используется odr из заданной единицы перевода, нелокальные переменные, определенные в этой единице перевода, никогда не могут быть инициализированы
Поскольку ни функция, ни переменная не использовались из b.cpp
ни c.cpp
их статические переменные могут быть неинициализированы (в отношении динамической инициализации), и, следовательно, побочные эффекты их инициализации могут быть не видны.
На практике я ожидал бы показанного, инициализирующего поведения, когда модули перевода статически связаны, и возможного неинициализирующего поведения, когда они динамически загружаются (разделяемая библиотека). Но ни один из них не гарантируется стандартом, так как он не определяет поведение разделяемых библиотек.
Других решений пока нет …