I had a discussion this morning with a colleague about static variable initialization order. He mentioned the Nifty/Schwarz counter and I\'m (sort of) puzzled. I understan
I believe it's guaranteed to work. According to the standard ($3.6.2/1): "Objects with static storage duration (3.7.1) shall be zero-initialized (8.5) before any other initialization takes place."
Since nifty_counter
has static storage duration, it gets initialized before initializer
is created, regardless of distribution across translation units.
Edit: After rereading the section in question, and considering input from @Tadeusz Kopec's comment, I'm less certain about whether it's well defined as it stands right now, but it is quite trivial to ensure that it is well-defined: remove the initialization from the definition of nifty_counter
, so it looks like:
static int nifty_counter;
Since it has static storage duration, it will be zero-initialized, even without specifying an intializer -- and removing the initializer removes any doubt about any other initialization taking place after the zero-initialization.
I think missing from this example is how the construction of Stream is avoided, this often is non-portable. Besides the nifty counter the initialisers role is to construct something like:
extern Stream in;
Where one compilation unit has the memory associated with that object, whether there is some special constructor before the in-place new operator is used, or in the cases I've seen the memory is allocated in another way to avoid any conflicts. It seems to me that is there is a no-op constructor on this stream then the ordering of whether the initialiser is called first or the no-op constructor is not defined.
To allocate an area of bytes is often non-portable for example for gnu iostream the space for cin is defined as:
typedef char fake_istream[sizeof(istream)] __attribute__ ((aligned(__alignof__(istream))))
...
fake_istream cin;
llvm uses:
_ALIGNAS_TYPE (__stdinbuf<char> ) static char __cin [sizeof(__stdinbuf <char>)];
Both make certain assumption about the space needed for the object. Where the Schwarz Counter initialises with a placement new:
new (&cin) istream(&buf)
Practically this doesn't look that portable.
I've noticed that some compilers like gnu, microsoft and AIX do have compiler extensions to influence static initialiser order:
init-priority
with the -f
flag and use __attribute__ ((init_priority (n)))
.