recent question引起我注意constexpr
has changed in C++14的方式。新功能是,如果其初始化程序由constexpr
构造函数组成,即使变量的类型不是文字类型,也可以在静态初始化阶段初始化具有静态存储持续时间的非局部变量。更准确地说,[basic.start.init]中的新措辞是:
对象
o
的常量初始值设定项是一个表达式,它是一个常量表达式,除了它还可以调用o
及其子对象的constexpr构造函数,即使这些对象属于非文字类类型[注意:这样的类可能有一个非平凡的析构函数 - 结束注释]。如果通过构造函数调用初始化具有静态或线程存储持续时间的对象,并且初始化full-expression是对象的常量初始化程序,则执行常量初始化 [...] ]
典型的例子是std::unique_ptr
,其中"永远不会更糟糕的是手写":
std::unique_ptr<int> p; // statically initialized by [unique.ptr.single.ctor],
// requires no code excution
int main()
{
p = std::make_unique<int>(100);
}
// p is destroyed eventually
在此添加之前,静态初始化的变量既可以是引用类型,也可以是文字对象类型,因此具有简单的析构函数。但是现在一个静态初始化的全局变量可以有一个非平凡的析构函数。
对于动态初始化的全局对象的析构函数,如何针对其他静态初始化的析构函数调用这样的析构函数调用,以及析构函数如何调用序列?
答案 0 :(得分:3)
考虑
如果一个对象是静态初始化的,那么该对象将被破坏 与对象动态初始化的顺序相同。
和
如果构造函数完成或动态初始化了 具有静态存储持续时间的对象在之前被排序 另外,对第二个析构函数的完成进行了排序 在第一个析构函数启动之前。
现在,
静态初始化应在任何动态之前执行 初始化发生。
显然,这回答了第一个问题:由于保证p
在执行任何动态初始化之前被初始化,因此在任何动态初始化对象被销毁之后,析构函数被称为。
基本上第二个问题,即几个静态初始化变量的析构的顺序,减少到这些初始化的顺序:
使用静态存储动态初始化非局部变量 持续时间是有序的或无序的。明确的定义 专门的类模板静态数据成员已订购 初始化。其他类模板静态数据成员(即 隐式或显式实例化的特化)具有无序性 初始化。 具有静态存储的其他非局部变量 持续时间有序初始化。
粗体句子包括所有静态初始化的对象,这些对象不是实例化类的静态数据成员。它们在一个翻译单元内订购:
在单个内定义的有序初始化变量 翻译单位应按其顺序初始化 翻译单位中的定义。
所以,总结一下:
以静态初始化为主体且不是实例化类的静态数据成员的变量将按照翻译文件中定义的相反顺序销毁。
......在销毁任何动态初始化对象后,这些变量总是被销毁。
然而,尽管可能存在争议性错误,但Clang和GCC似乎都没有采用这种方式实现它:Demo。
答案 1 :(得分:1)
[basic.start.term]/1
(N4140)说:
如果对象是静态初始化的,则对象的破坏顺序与对象动态初始化的顺序相同。
据我所知,这意味着为了确定破坏的顺序,所有静态初始化都被视为动态(有序或无序),而析构函数的调用顺序与此初始化相反。