Сегодня утром у меня была дискуссия с коллегой о статическом порядке инициализации переменных. Он упомянул счетчик Nifty/Schwarz, и я (вроде) озадачен. Я понимаю, как это работает, но я не уверен, стандартно ли это соответствует стандарту.
Предположим, что 3 следующих файла (первые два являются copy-pasta'd из Дополнительные Идиомы С++):
//Stream.hpp
class StreamInitializer;
class Stream {
friend class StreamInitializer;
public:
Stream () {
// Constructor must be called before use.
}
};
static class StreamInitializer {
public:
StreamInitializer ();
~StreamInitializer ();
} initializer; //Note object here in the header.
//Stream.cpp
static int nifty_counter = 0;
// The counter is initialized at load-time i.e.,
// before any of the static objects are initialized.
StreamInitializer::StreamInitializer ()
{
if (0 == nifty_counter++)
{
// Initialize Stream object static members.
}
}
StreamInitializer::~StreamInitializer ()
{
if (0 == --nifty_counter)
{
// Clean-up.
}
}
// Program.cpp
#include "Stream.hpp" // initializer increments "nifty_counter" from 0 to 1.
// Rest of code...
int main ( int, char ** ) { ... }
... и вот проблема! Существуют две статические переменные:
- "nifty_counter" в
Stream.cpp
; и - "инициализатор" в
Program.cpp
.
Поскольку две переменные находятся в двух разных единицах компиляции, официальная гарантия (AFAIK) отсутствует, если nifty_counter
инициализируется до 0 до вызова конструктора initializer
.
Я могу думать о двух быстрых решениях как о двух, почему это "работает":
- современные компиляторы достаточно умны, чтобы разрешить зависимость между двумя переменными и поместить код в соответствующем порядке в исполняемый файл (крайне маловероятно);
-
nifty_counter
на самом деле инициализируется в режиме "load-time", как говорится в статье, и его значение уже помещено в "сегмент данных" в исполняемом файле, поэтому оно всегда инициализируется "перед запуском любого кода" (высоко вероятно).
Оба они кажутся мне такими, как они зависят от некоторой неофициальной, но возможной реализации. Является ли этот стандарт совместимым или это просто "настолько вероятно работает", что мы не должны беспокоиться об этом?