初始化模板化,递归,POD结构

时间:2015-08-19 02:10:16

标签: c++ templates initialization

我正在尝试使用模板递归来生成嵌套的POD结构,我遇到了一些我并不期待的行为。这是一个简化的测试用例:

#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;
}

我希望这段代码能够输出

5
5

确实,这是我用GCC 4.8.4和5.1编译时生成的内容。

但是,使用Clang(3.5或3.7)或Visual Studio 2010进行编译会导致

5
0

我的代码或我对它的理解是否在某种程度上是错误的,或者Clang和Visual Studio是否都有错误会导致相同的错误输出?

1 个答案:

答案 0 :(得分:3)

我认为两个编译器都符合,因为静态变量的初始化顺序是未指定的。最清晰的陈述来自[basic.start.init]中的注释:

  

[注意:因此,如果对象obj1的初始化引用了命名空间范围的对象obj2   可能需要动态初始化并在稍后的同一翻译单元中定义,它是未指定的   是否使用obj2的值将是完全初始化的obj2的值(因为obj2是静态的   初始化)或将是obj2的值仅为零初始化。例如,

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]

在我们的情况下,Builder<start, 1>::value已静态初始化,但其他所有内容都是动态未初始化的 - 因此未指定使用完全初始化的Builder<start, 1>::value

一种解决方法是在第一次使用习惯用法时使用该构造并执行类似的操作(为了简单起见,我冒昧地从Builder拉出RecursiveStruct - 它表现出相同的行为方式):< / p>

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();

这会在两个编译器上打印5