初始化模板化的、递归的、POD结构

Initializing templated, recursive, POD struct

本文关键字:POD 结构 递归 初始化      更新时间:2023-10-16

我正在尝试使用模板递归来生成嵌套的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不知何故都有导致相同错误输出的错误?

我认为两个编译器都是符合的,因为静态变量的初始化顺序未指定。最清晰的语句来自[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

一种解决方法是在第一次使用时使用结构,并做一些类似的事情(为了简单起见,我冒昧地将BuilderRecursiveStruct中拉出来-无论哪种方式都表现出相同的行为):

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