考虑以下 C++ 代码示例:
#include<iostream>
class B {
public:
int val;
B(int v):val(v){
std::cout<< val << "\n";
}
};
template<typename T>
class A {
public:
static B b;
};
template<typename T>
B A<T>::b = B(1);
int main() {
A<int>::b.val;
return 0;
}
在 GCC 11.4.0 上,使用
g++ main.cc -g
进行构建,得到输出 1
。
在 Clang 14.0.0 上,使用
clang++ main.cc -g
构建得到 segfault
。
Ubuntu 22.04.4 LTS
无法理解这种行为的原因,将非常感谢任何帮助。
该程序具有未定义的行为,因为您使用
std::cout
时没有任何保证它已初始化。
静态存储持续时间变量
A<int>::b
的初始化是动态初始化,因为B
的构造函数不是constexpr
(除非实现选择进行静态初始化)。而且因为它是从模板实例化的非局部变量,所以它具有无序动态初始化,这意味着它的初始化与非局部变量的任何其他动态初始化都是无序的。
像
std::cout
这样的标准流不会自动初始化和使用。相反,标头 <iostream>
的行为就像声明了 std::ios_base::Init
类型的全局静态存储持续时间变量。当初始化该类型的变量时,它将初始化标准流。
因为
A<int>::b
的初始化与该 std::ios_base::Init
实例的初始化是无序的,所以它可能发生在 std::cout
的初始化之前。
为了确保在需要使用流时初始化它们,您需要自己初始化一个
std::ios_base::Init
实例:
B(int v):val(v){
std::ios_base::Init _;
std::cout<< val << "\n";
}