为什么此循环模板实例合法?

单打

我不明白编译器在这里做什么:

#include <iostream>
using namespace std;

// non-default-constructable struct
struct X
{
    X(int v) : x(v) {}
    const int x;
};

template< typename T>
struct A
{
    static const X a;
};

// trigger a compiler error if we try to instantiate the default template
template< typename T >
const X A<T>::a;


template<>
struct A<int>
{
    static const X a;
};

template<>
struct A<float>
{
    static const X a;
};

// is this not infinitely circular?
const X A<int>::a = X(A<float>::a.x + 1);
const X A<float>::a = X(A<int>::a.x + 1);

int main() {
    // error as expected, A<bool>::a cannot be default-constructed
    // cout << A<bool>::a.x << endl; 

    // this compiles and prints "1 2"
    cout << A<int>::a.x << " " << A<float>::a.x << endl;
    return 0;
}

我本来希望这两个专门的定义a会产生编译器错误,因为它们都使用另一个的值进行了初始化,甚至没有默认构造函数可以使用。但是显然,这可以用ideone进行打印1 2那么,编译器如何得出结论,X应使用这些值初始化的两个实例

Serge Ballesta

这恰好是非局部变量初始化的副作用。该标准说:

3.6.2非局部变量的初始化[basic.start.init]

……
在进行任何其他初始化之前,应将具有静态存储持续时间(3.7.1)或线程存储持续时间(3.7.2)的变量初始化为零(8.5)
。在进行任何动态初始化之前。如果变量是隐式或显式实例化的特殊化,则动态地初始化具有静态存储持续时间的非局部变量是无序的,否则是有序的[注意:显式专用的静态数据成员或变量模板的特殊化已对初始化进行了排序。—尾注]在单个转换单元中定义了有序初始化的变量应按其在转换单元中的定义顺序进行初始化。

这就是这里发生的情况:

  • A<int>::a并且A<float>::a都初始化为0
  • 然后按照定义的顺序对其进行初始化
    • 首先A<int>::a读取A<float>::a由于先前的0初始化而其中为0的内容,然后加1并用1完全初始化
    • 然后A<float>::a获取现在完全初始化的A<int>::a值为1,加1并用2完全初始化

这意味着这是一个格式正确的程序。

但同一段后来说:

如果对象obj1的初始化引用了可能需要动态初始化并在以后在同一转换单元中定义的命名空间范围的对象obj2,则未指定使用的obj2的值是否将是完全初始化的obj2的值(因为obj2为静态初始化)或将只是零初始化的obj2的值

所以我不确定如果A<int>::afirst的动态初始化触发的动态初始化,则输出是否需要为1 2,或者是否可以为2,1 A<float>::a

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章