I was fooling around with static class variables, and I ran into something unexpected that I don't understand.
Stroustrup's book TC++PL4 says in 15.4.1 Initialization of Nonlocal Variables, "There is no guaranteed order of initialization of global variables in different translation units." But see that's for multiple translation units; I wasn't testing dependencies between files, this was in a single file. The same section says "In principle, a variable defined outside any function (that is, global, namespace, and class static variables) is initialized before main()
is invoked. Such nonlocal variables in a translation unit are initialized in their definition order.
If I set up this at the top of the file:
class Test1 {
public:
static double test1;
};
class Test2 {
public:
static double test2;
};
class Test3 {
public:
static double test3;
};
I can do this:
double Test2::test2 = Test1::test1;
double Test1::test1 = 1.125;
And put this statement in main to check the values:
cout << Test1::test1 << " " << Test2::test2;
And both values will be 1.125. I understand that both variables are in scope to be defined at that point, but if they are initialized in definition order how is it that a statement above is using the data from a statement below? Also, the order of class definitions at the top changes nothing in any of these cases.
If I use this arrangement:
double Test2::test2 = Test1::test1;
double Test3::test3 = Test2::test2;
double Test1::test1 = 1.125;
They will also all be 1.125.
But if I do this:
double Test3::test3 = Test2::test2;
double Test2::test2 = Test1::test1;
double Test1::test1 = 1.125;
Test3::test3
will be 0.
It seems like it can look for another definition one layer in, but if that definition needs yet another definition it stops and just assigns zero. But I don't know what's really happening.
Does anyone know why it does this? I tried this in the C++ '14 mode of http://cpp.sh (GCC) and in VSE 2012 with consistent results.
EDIT: See, I keep seeing everywhere that you can't predict it, but this observation seems to be glossing over the apparent fact that there's a difference between uncertainty across files and the (possibly nonexistent?) uncertainty within a single file. Stroustrup basically said there's a difference, but static variables are weird.
I had a thought that it's possible that a class definition looks outside the original file to initialize the static data member even in the case of a single file so that in terms of the compilation logic the file loops around to find itself again, generating UB. If a compiler were programmed to assign zero instead of jumping to a third external variable the result would be predictable and more stable, but I don't have any proof that a file can externally reference itself like this.
EDIT 2: It appears to be a matter of static and dynamic initialization, where static initialization assigns zero or a given constant at compile time and dynamic then uses those values (like Mark B said) as in this: What is dynamic initialization of object in c++? But I have to ask whether it's UB or not, still.
LAST EDIT: It wasn't hard to find information, the behavior is very well founded and not UB, see here: http://en.cppreference.com/w/cpp/language/initialization
So what's happening here is that there are effectively two phases of the initialization: static and dynamic. The compiler can see that test1
is a constant and embeds that constant in the binary, default it to the value. It can't do so for test2
and test3
, so those get values of 0
in the binary.
Then before main
starts up, the dynamic initialization phase starts, puts the 0 value from test2
into test3
and then the 1.25 constant from test1
into test2
.
All that said never rely on this and always initialize your variables in a sane order.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With