我有一些遗留代码,我需要为消息添加一个新类(这与我的问题无关)。但事实证明,我需要声明一个空构造函数,以便初始化一些静态。不是默认构造函数或编译器提供的,而是用户定义的空。我试图将代码减少到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检查了它 - 行为是一样的。我非常感谢标准/文档的链接。
答案 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>()
。