【问题标题】:Why is this static member not constructed even though it exists?为什么这个静态成员即使存在也没有构造?
【发布时间】:2018-08-11 13:50:58
【问题描述】:

前几天我遇到了一些意想不到的行为,并将其简化为这几行代码。我在 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++ 的行为与以前一样。所以我在这里的怀疑是行为是未定义的,而不仅仅是意外。我对语言标准措辞不够熟悉,无法准确地说出这种情况下的违规行为。但这肯定违背了我对静态成员和继承如何工作的直觉。

【问题讨论】:

  • 您的第一个代码从不实例化 B 对象。
  • @NeilButterworth instantiating B object won't help。应该在某处使用X::data。喜欢static_cast&lt;void&gt;(B::data);

标签: c++ templates inheritance static


【解决方案1】:

根据[temp.inst]/3

...特别是,静态数据成员的初始化(以及任何相关的副作用)不会发生,除非静态数据成员本身的使用方式需要存在静态数据成员的定义。

[expr.context]/1:

[ 注意:在未计算的操作数中,可以命名非静态类成员([expr.prim.id]),并且对象或函数的命名本身并不要求提供定义([basic .def.odr])。 ... — 尾注 ]

X&lt;A&gt;::data 仅用作sizeof 的操作数,这是一个未计算的操作数,因此X&lt;A&gt;::data 未初始化。

对于typeid的情况,我认为是GCC的bug。

【讨论】:

    【解决方案2】:

    访问结构的静态成员不会实例化它,因此在您的情况下,struct X&lt;A&gt; 的构造函数永远不会被调用。

    您甚至可以简化代码以尝试不使用模板,因为它不会影响此处的结果。

    【讨论】:

      猜你喜欢
      • 2011-11-16
      • 2022-01-22
      • 2019-09-12
      • 2011-11-10
      • 1970-01-01
      • 2013-09-18
      • 2023-03-06
      • 1970-01-01
      • 2011-08-08
      相关资源
      最近更新 更多