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是否都有错误导致相同的错误输出?
我认为两个编译器都是一致的,因为静态变量的初始化顺序未指定。最清晰的说法来自于[basic.start.init]中的注释:
\n\n\n\n\n[ 注意:因此,如果对象 obj1 的初始化引用了命名空间范围的对象 obj2,\n 可能需要动态初始化并稍后在同一翻译单元中定义,则未指定\n 使用的 obj2 的值是否将是完全初始化的 obj2 的值(因为 obj2 是静态初始化的)或者只是零初始化的 obj2 的值。例如,
\n\nRun Code Online (Sandbox Code Playgroud)\n\ninline 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\xe2\x80\x94结束注]
\n
在我们的例子中,Builder<start, 1>::value是静态初始化的,但其他所有内容都是动态未初始化的 - 因此未指定Builder<start, 1>::value是否使用完全初始化的。
解决方法是使用首次使用习惯用法的构造并执行类似的操作(为了简单起见,我冒昧地退出了Builder-RecursiveStruct无论哪种方式,它都表现出相同的行为):
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();\nRun Code Online (Sandbox Code Playgroud)\n\n这会5在两个编译器上打印。