Reputation: 8273
I don't understand what the compiler's doing here:
#include <iostream>
using namespace std;
// non-default-constructable struct
struct X
{
X(int v) : x(v) {}
const int x;
};
template< typename T>
struct A
{
static const X a;
};
// trigger a compiler error if we try to instantiate the default template
template< typename T >
const X A<T>::a;
template<>
struct A<int>
{
static const X a;
};
template<>
struct A<float>
{
static const X a;
};
// is this not infinitely circular?
const X A<int>::a = X(A<float>::a.x + 1);
const X A<float>::a = X(A<int>::a.x + 1);
int main() {
// error as expected, A<bool>::a cannot be default-constructed
// cout << A<bool>::a.x << endl;
// this compiles and prints "1 2"
cout << A<int>::a.x << " " << A<float>::a.x << endl;
return 0;
}
I would have expected the two specialized definitions of a
to generate a compiler error, since they are both initialized using the value of the other, and there isn't even a default constructor to fall back on. But apparently, this compiles in ideone and prints 1 2
. So how did the compiler arrive to the conclusion that the two instances of X
should be initialized with these values?
Upvotes: 3
Views: 75
Reputation: 149035
That just happens to be a side effect of the initialization of non-local variables. The standard says:
3.6.2 Initialization of non-local variables [basic.start.init]
...
... Variables with static storage duration (3.7.1) or thread storage duration (3.7.2) shall be zero-initialized (8.5) before any other initialization takes place...
... Static initialization shall be performed before any dynamic initialization takes place. Dynamic initialization of a non-local variable with static storage duration is unordered if the variable is an implicitly or explicitly instantiated specialization, and otherwise is ordered [ Note: an explicitly specialized static data member or variable template specialization has ordered initialization. —end note ] Variables with ordered initialization defined within a single translation unit shall be initialized in the order of their definitions in the translation unit.
That's exactly what happens here:
A<int>::a
and A<float>::a
are both 0 initializedA<int>::a
reads what lies in A<float>::a
which is 0 because of the previous 0 initialization, adds 1 and is fully initialized with 1A<float>::a
gets the value of the now fully initialized A<int>::a
which is 1, adds 1 and is fully initialized with 2That means that this is a well formed program.
But same paragraph says later:
if the initialization of an object obj1 refers to an object obj2 of namespace scope potentially requiring dynamic initialization and defined later in the same translation unit, it is unspecified whether the value of obj2 used will be the value of the fully initialized obj2 (because obj2 was statically initialized) or will be the value of obj2 merely zero-initialized
So I am unsure whether the output is required to be 1 2, or whether it could be 2, 1 if the dynamic initialization of A<int>::a
first triggers the dynamic initialization of A<float>::a
Upvotes: 4