前几天,我遇到了一些意外行为,并将其简化为以下几行代码。我在VC ++ 19.0,Clang 3.8和GCC 5.4.0以及8.2.0上进行了测试。在每种情况下,输出仅为1
,而我曾期望它以Hello
开头并以Goodbye
结束。
#include <iostream>
template <class T> struct X { static T data; };
template <class T> T X<T>::data;
struct A
{
A()
{
std::cout << "Hello" << std::endl;
}
~A()
{
std::cout << "Goodbye" << std::endl;
}
};
struct B : X<A> { };
int main(int argc, char **argv)
{
std::cout << sizeof(B::data) << std::endl;
}
显然B::data
存在,但从未调用过其构造函数和析构函数。有趣的是,如果我将其添加到测试中
assert(typeid(B::data) == typeid(A));
GCC的行为符合我最初的预期,但Clang和VC ++的行为均与以前相同。所以我在这里的怀疑是行为是不确定的,而不仅仅是意外的。我对语言标准的措辞还不熟悉,无法为自己准确说明这种情况下的违规行为。但这肯定与我对静态成员和继承如何工作的直觉相抵触。
答案 0 :(得分:1)
...尤其是,除非静态数据成员本身以要求静态数据成员的定义存在的方式使用,否则不会发生静态数据成员的初始化(以及任何相关的副作用)。 / p>
[注:在未求值的操作数中,可以将非静态类成员命名为([expr.prim.id]),并且对象或函数的命名本身并不要求提供定义([基本.def.odr])。 ... —注释]
X<A>::data
仅用作sizeof
的操作数,它是未评估的操作数,因此X<A>::data
未初始化。
对于typeid
,我认为这是GCC错误。
答案 1 :(得分:0)
访问结构的静态成员不会实例化它,因此永远不会调用struct X<A>
的构造函数。
您甚至可以简化代码以尝试不使用模板,因为它不会影响此处的结果。