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

rkj*_*nsn 8 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;
}
Run Code Online (Sandbox Code Playgroud)

我希望这段代码能输出

5
5
Run Code Online (Sandbox Code Playgroud)

实际上,它是我用GCC 4.8.4和5.1编译时生成的.

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

5
0
Run Code Online (Sandbox Code Playgroud)

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

Bar*_*rry 3

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

\n\n
\n

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

\n\n
inline double fd() { return 1.0; }\nextern double d1;\ndouble d2 = d1; // unspecified:\n// may be statically initialized to 0.0 or\n// dynamically initialized to 0.0 if d1 is\n// dynamically initialized, or 1.0 otherwise\ndouble d1 = fd(); // may be initialized statically or dynamically to 1.0\n
Run Code Online (Sandbox Code Playgroud)\n\n

\xe2\x80\x94结束注]

\n
\n\n

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

\n\n

解决方法是使用首次使用习惯用法的构造并执行类似的操作(为了简单起见,我冒昧地退出了Builder-RecursiveStruct无论哪种方式,它都表现出相同的行为):

\n\n
template <std::size_t start, std::size_t length>\nstruct Builder\n{\n    static const size_t mid = start + length / 2;\n    static const size_t end = start + length;    \n\n    static const Builder value() {\n        static const Builder value_{ \n            Builder<start, mid - start>::value(), \n            Builder<mid, end - mid>::value() \n        };\n        return value_;\n    }\n\n    Builder<start, mid - start> left;\n    Builder<mid, end - mid> right;\n};\n\ntemplate <std::size_t start>\nstruct Builder<start, 1> {\n    static const Builder value() {\n        static const Builder value_{5};\n        return value_;\n    }\n\n    int data;\n};\n\ntemplate<std::size_t size>\nstruct RecursiveStruct {\npublic:\n    static const Builder<0, size> result;\n};\n\ntemplate <std::size_t size>\nconst Builder<0, size> RecursiveStruct<size>::result = Builder<0, size>::value();\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n