类静态变量初始化的规则是什么?

时间:2017-05-04 12:01:26

标签: c++ static crtp

我有一些遗留代码,我需要为消息添加一个新类(这与我的问题无关)。但事实证明,我需要声明一个空构造函数,以便初始化一些静态。不是默认构造函数或编译器提供的,而是用户定义的空。我试图将代码减少到MWE,这就是我得到的:

#include <iostream>

using namespace std;

struct Test
{
    Test() {cout << "Test::Test()" << "\n";}
    void dummy(){}
};

template<typename T>
struct Message
{
    Message()
    {
        test.dummy(); // this call have to be here in order to initialize Test, but why?
    }

    static Test test;
};

template<typename T>
Test Message<T>::test;

struct A : public Message<A>
{
    //A(){} // uncomment this (and comment the default one) to call the Test constructor
    A() = default;
};

int main()
{
}

这就是发生的事情:

  • 程序本身为空,即没有创建实例。
  • A类有一个CRTP,这似乎对这个例子至关重要。
  • A的基础有一个静态声明,我期待它的构造函数被调用。
  • 对函数进行虚拟调用,什么都不做,但也很关键。

问题是如果我不提供自定义构造函数,那么永远不会调用静态构造函数。我无法理解为什么需要这个?与默认或编译器生成的有什么区别?为什么我需要调用虚函数?

我相信这是有规则的。我用不同版本的gcc和clang检查了它 - 行为是一样的。我非常感谢标准/文档的链接。

2 个答案:

答案 0 :(得分:4)

如果您保留A构造函数默认值并且从不调用它,则无需生成它,因此无需创建test。如果您在定义时明确默认,请调用A构造函数或访问A::test,它将被正确初始化。

  

12.1构造函数[class.ctor]

     

7   当使用odr-used(3.2)创建其类类型(1.8)的对象或在第一次声明后显式默认为默认构造函数时,默认构造函数是默认的,并且未定义为已删除。

struct A : public Message<A>
{
    A() = default; // no constructor is emitted unless A is instantiated

    A(); // declaration
};

A::A() = default; // explicit default definition

int
main()
{
    A a; // instantiation
    A::test; // just explicitly access test so it is initialized regardless of A constructor
}

答案 1 :(得分:3)

C ++ 14 [temp.inst] / 2:

  

除非已经显式实例化或明确专门化了类模板或成员模板的成员,否则在需要成员定义存在的上下文中引用特化时,将隐式实例化成员的特化;特别是,除非静态数据成员本身以需要静态数据成员定义存在的方式使用,否则不会发生静态数据成员的初始化(以及任何相关的副作用)。

这清楚地表明Message<A>::test将不会被初始化,除非它以需要其定义存在的方式使用。

程序中唯一需要定义的表达式是test.dummy()的构造函数中的Message<A>;因此,如果删除该表达式,则不得初始化test

对于存在test.dummy()的情况,请注意它位于模板函数内,Message<A>的构造函数。如果从未实例化此构造函数,则不会考虑test.dummy()

正如VTT所指出的,[class.ctor]表示A的显式默认构造函数意味着除非使用A,否则不会定义构造函数。

你的代码没有使用A,因此没有定义A的构造函数,因此没有基类构造函数的调用(只有在{{1}时才会发生已经定义了构造函数),因此构造函数模板A未实例化,因此不需要存在Message<A>()