Why is this templated templated instance legal?

I don't understand what the compiler is 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 expect the two specialized definitions to a

throw a compiler error, since they both are initialized to the value of the other, and not even a default constructor is returned. But apparently this compiles in idea and prints 1 2

. So how did the compiler come to the conclusion that two instances X

should be initialized with these values?

+3


source to share


1 answer


This is just a side effect of initializing non-local variables. The standard says:

3.6.2 Initializing Non-Local Variables [basic.start.init]

...
... Variables with static storage duration (3.7.1) or thread storage duration (3.7.2) must be zero (8.5) before any other initialization ...
... Static initialization must be performed before any dynamic initialization begins ... Dynamic initialization of a non-local variable with static storage duration is unordered if the variable is an implicitly or explicitly created specialization and is otherwise ordered [Note: An explicitly specialized static data member or variable template specialization ordered initialization. -end note] Variables with ordered initialization defined within a single translation unit must be initialized in the order in which they are defined in the translation unit.

What exactly is happening here:

  • A<int>::a

    and A<float>::a

    both are initialized as
  • They are then initialized in the order of their definition.
    • first A<int>::a

      reads what lies in A<float>::a

      , which is 0 due to previous initialization of 0, adds 1 and is fully initialized to 1
    • then A<float>::a

      gets the value fully initialized A<int>::a

      which is 1, add 1 and fully initialized 2


This means that it is a well-formed program.

But the same paragraph says later:

if the initialization of obj1 belongs to a namespace object obj2 potentially requiring dynamic initialization and is defined later in the same translation unit, it is not determined whether the value of obj2 will be used by the value of the fully initialized obj2 (since obj2 was statically initialized) or will be the value of obj2, just null initialized

So I'm not sure if the output is 1 2 or maybe 2, 1 if dynamic initialization A<int>::a

starts dynamic initialization firstA<float>::a

+4


source







All Articles