初始化模板内的静态成员
Initialization of a static member inside a template
这里有一个最小的例子:
#include <iostream>
struct B {
B() { x = 42; }
static int x;
};
int B::x;
template <int N>
struct A {
int foo() { return b.x; }
static B b;
};
template<int N>
B A<N>::b;
//template struct A<2>; // explicit instantiation with N = 2 (!)
int main(int argc, char **argv) {
std::cout << A<1>().foo() << std::endl;
return 0;
}
此程序使用g++4.9.2编写42,但使用Visual Studio 2015 RC编写0。此外,如果我取消注释显式实例化,VS2015RC还会给出42,这很有趣,因为这里的模板参数与main
函数中使用的参数不同。
这是个虫子吗?我假设g++是正确的,因为在foo
中有对b
的引用,所以应该调用B
的构造函数。
编辑:有一个简单的解决方法-如果B
中有一个在A
中引用的非静态变量,VS2015RC将正确编译:
// ...
struct B {
B() { x = 42; }
static int x;
int y; // <- non-static variable
};
// ...
template <int N>
struct A {
int foo() { b.y; return b.x; } // <- reference to b.y
static B b;
};
这似乎是有效的,尽管b.y
作为一种声明,显然是NOP。
从〔basic.start.init〕:
静态存储持续时间为(3.7.1(或线程存储持续时间(3.7.2(的变量应初始化为零(8.5(在进行任何其他初始化之前。对象o的常量初始值设定项是常量表达式,除了它还可以调用o及其子对象的constexpr构造函数之外如果这些对象是非文字类类型。[…]
零初始化和常量初始化统称为静态初始化;所有其他初始化动态初始化。应在进行任何动态初始化之前进行静态初始化。
在我们的例子中,b
是静态初始化的,但b.x
是动态初始化的(构造函数不是constexpr(。但我们也有:
实现定义了是否使用静态存储对非本地变量进行动态初始化持续时间在main的第一个语句之前完成。如果初始化推迟到某个时间点在main的第一个语句之后,它应该发生在任何函数或变量的第一次odr使用(3.2(之前在与要初始化的变量相同的转换单元中定义。
Odr使用的手段,来自〔basic.def.Odr〕:
变量x的名称显示为可能求值的表达式ex,除非应用从左值到右值的转换(4.1(到x产生了一个常数表达式(5.20(,它不会调用任何非平凡的功能,如果[…]
但是计算b.x
并不能得到一个常数表达式,所以我们可以到此为止——b.x
是A<N>::foo()
使用的odr,这也是第一个odur使用。因此,虽然初始化不必在main()
之前发生,但它必须在foo()
之前发生。所以如果你得到0,那就是编译器错误。
我倾向于这样写代码:
struct B {
B() {}
static int x;
};
int B::x = 42;
毕竟,static(x(是在最后一行定义的(因此应该初始化(。把初始化放在B的构造函数中意味着每次构造B时,静态x(只有一个!(都会被重新初始化。有一个静态,你应该只初始化一次。
- C++模板类静态成员初始化
- C :(不重复)积分静态成员初始化(不仅是声明!),导致链接器错误,原因
- 向模板化静态成员初始化添加和不添加'typename'都会出错
- 模板类中的静态成员初始化
- 模板专用化和静态成员初始化
- 静态内联方法无需静态成员初始化
- 静态成员初始化链接错误
- 类内静态成员初始化
- 跨系统的静态成员初始化
- C++类静态成员初始化
- gcc中模板的非延迟静态成员初始化
- Clang++Xcode 4.4非静态成员初始化和移动构造函数
- 为什么静态成员初始化需要"typed again"?
- 即使启用了 C++11,静态成员初始化也不适用于 GCC
- C++静态成员初始化顺序
- 常量静态成员初始化 - 类定义内部与外部类定义
- 如何保证 const 静态成员初始化顺序
- 在单独的库中使用 CRTP 进行静态成员初始化
- 静态成员初始化和可变模板
- 模板化的外部类静态成员初始化