Initializing a Template, Recursive POD Structure

I experimented using template recursion to create nested POD structures, and I came across some kind of behavior that I did not expect. Here's a simplified example:

#include <cstddef> template<std::size_t size> struct RecursiveStruct { public: template <std::size_t start, std::size_t length> struct Builder { static const Builder value; static const size_t mid = start + length / 2; static const size_t end = start + length; Builder<start, mid - start> left; Builder<mid, end - mid> right; }; template <std::size_t start> struct Builder<start, 1> { static const Builder value; int data; }; static const Builder<0, size> result; }; template<std::size_t size> const typename RecursiveStruct<size>::template Builder<0, size> RecursiveStruct<size>::result = Builder<0, size>::value; template<std::size_t size> template<std::size_t start, std::size_t length> const typename RecursiveStruct<size>::template Builder<start, length> RecursiveStruct<size>::Builder<start, length>::value = { Builder<start, mid - start>::value, Builder<mid, end - mid>::value }; template<std::size_t size> template <std::size_t start> const typename RecursiveStruct<size>::template Builder<start, 1> RecursiveStruct<size>::Builder<start, 1>::value = { 5 }; //////////////////////////////////////////////////////// #include <iostream> using std::cout; using std::endl; using std::size_t; int main() { cout << RecursiveStruct<1>::result.data << endl; cout << RecursiveStruct<2>::result.left.data << endl; return 0; } 

I expect this code to output

 5 5 

In fact, this is what is generated when compiling with GCC 4.8.4 and 5.1.

However, compiling with Clang (3.5 or 3.7) or Visual Studio 2010 produces results

 5 0 

Is my code or my understanding of it incorrect in any way, or does Clang and Visual Studio somehow have errors that lead to the same erroneous result?

+8
c ++ initialization templates
source share
1 answer

I think that both compilers correspond, since the order of initialization of static variables is not specified. The clearest statement comes from a note in [basic.start.init]:

[Note: as a result, if the initialization of the object obj1 refers to the object obj2 of the namespace area potentially requiring dynamic initialization and is determined later in the same translation unit, it is not determined whether the value 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 zero initialized. For example,

 inline double fd() { return 1.0; } extern double d1; double d2 = d1; // unspecified: // may be statically initialized to 0.0 or // dynamically initialized to 0.0 if d1 is // dynamically initialized, or 1.0 otherwise double d1 = fd(); // may be initialized statically or dynamically to 1.0 

-end note]

In our case, Builder<start, 1>::value was statically initialized, but everything else is dynamically uninitialized, so it is not specified with respect to the fully initialized Builder<start, 1>::value .

The workaround is to use the construct on the first use idiom and do something like (I just let the Builder get pulled out of the RecursiveStruct for simplicity - it exhibits the same behavior anyway):

 template <std::size_t start, std::size_t length> struct Builder { static const size_t mid = start + length / 2; static const size_t end = start + length; static const Builder value() { static const Builder value_{ Builder<start, mid - start>::value(), Builder<mid, end - mid>::value() }; return value_; } Builder<start, mid - start> left; Builder<mid, end - mid> right; }; template <std::size_t start> struct Builder<start, 1> { static const Builder value() { static const Builder value_{5}; return value_; } int data; }; template<std::size_t size> struct RecursiveStruct { public: static const Builder<0, size> result; }; template <std::size_t size> const Builder<0, size> RecursiveStruct<size>::result = Builder<0, size>::value(); 

Prints 5 on both compilers.

+3
source share

All Articles