为什么这个循环模板实例化是合法的?
Why is this circular template instantiation legal?
我不明白编译器在这里做什么:
#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
的两个实例应该用这些值初始化的结论的呢?
这恰好是非局部变量初始化的副作用。标准说:
3.6.2 Initialization of non-local variables [basic.start.init]
...
... Variables with static storage duration (3.7.1) or thread storage duration (3.7.2) shall be zero-initialized (8.5)
before any other initialization takes place...
... Static initialization shall be performed before any dynamic initialization takes place.
Dynamic initialization of a non-local variable with static storage duration is unordered if the variable is an
implicitly or explicitly instantiated specialization, and otherwise is ordered [ Note: an explicitly specialized
static data member or variable template specialization has ordered initialization. —end note ] Variables
with ordered initialization defined within a single translation unit shall be initialized in the order of their
definitions in the translation unit.
这正是这里发生的事情:
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 完全初始化
这意味着这是一个结构良好的程序。
但同段后面说:
if the initialization of an object obj1 refers to an object obj2 of namespace scope
potentially requiring dynamic initialization and defined later in the same translation unit, it is unspecified
whether the value of obj2 used will be the value of the fully initialized obj2 (because obj2 was statically
initialized) or will be the value of obj2 merely zero-initialized
所以我不确定是否要求输出为1 2,或者如果A<int>::a
的动态初始化先触发A<float>::a
[=的动态初始化是否可以是2, 1 20=]
我不明白编译器在这里做什么:
#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
的两个实例应该用这些值初始化的结论的呢?
这恰好是非局部变量初始化的副作用。标准说:
3.6.2 Initialization of non-local variables [basic.start.init]
...
... Variables with static storage duration (3.7.1) or thread storage duration (3.7.2) shall be zero-initialized (8.5) before any other initialization takes place...
... Static initialization shall be performed before any dynamic initialization takes place. Dynamic initialization of a non-local variable with static storage duration is unordered if the variable is an implicitly or explicitly instantiated specialization, and otherwise is ordered [ Note: an explicitly specialized static data member or variable template specialization has ordered initialization. —end note ] Variables with ordered initialization defined within a single translation unit shall be initialized in the order of their definitions in the translation unit.
这正是这里发生的事情:
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 完全初始化
- 首先
这意味着这是一个结构良好的程序。
但同段后面说:
if the initialization of an object obj1 refers to an object obj2 of namespace scope potentially requiring dynamic initialization and defined later in the same translation unit, it is unspecified whether the value of obj2 used will be the value of the fully initialized obj2 (because obj2 was statically initialized) or will be the value of obj2 merely zero-initialized
所以我不确定是否要求输出为1 2,或者如果A<int>::a
的动态初始化先触发A<float>::a
[=的动态初始化是否可以是2, 1 20=]