静态初始化的非字面量对象的销毁顺序

11
一道最近的问题引起了我的关注,它涉及到C++14中constexpr如何改变。新特性是,如果一个具有静态存储期的非局部变量的初始化器由一个constexpr构造函数组成,即使变量的类型不是字面类型,也可以在静态初始化阶段进行初始化。更准确地说,在[basic.start.init]中的新措辞为:

对象o常量初始化器是指一个常量表达式,但它也可以调用o及其子对象的constexpr构造函数,即使这些对象是非字面类类型[注意: 这样的类可能有一个非平凡的析构函数 - end note]。如果静态或线程存储期的对象通过构造函数调用进行初始化,并且初始化完整表达式是对象的常量初始化器,则执行常量初始化[...]。

典型的例子是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

在这个补充之前,静态初始化的变量要么是引用类型,要么是字面对象类型,因此具有平凡析构函数。但是现在,静态初始化的全局变量可以具有非平凡的析构函数。
这样的析构函数调用与动态初始化的全局对象的析构函数、其他静态初始化的对象的析构函数的顺序如何,以及析构函数的调用顺序如何?

1
这并不是C++14新出现的特性;即使以前的措辞也允许这种用法。(例如,std::mutex有一个constexpr构造函数,以便利用静态初始化。) - T.C.
@T.C.: 感谢澄清! - Kerrek SB
2个回答

4
考虑以下内容:
如果一个对象是静态初始化的,那么该对象的销毁顺序与动态初始化一样。
如果具有静态存储期的对象的构造函数或动态初始化完成在另一个对象之前,那么第二个对象的析构函数完成将在第一个对象的析构函数启动之前完成。
现在,必须在进行任何动态初始化之前执行静态初始化。
很明显,这回答了第一个问题:由于保证在执行任何动态初始化之前对p进行初始化,因此在销毁任何动态初始化对象之后调用析构函数。
基本上,第二个问题,即多个静态初始化变量的销毁顺序如何,可以简化为这些变量初始化的顺序。
具有静态存储期的非局部变量的动态初始化可以是有序或无序的。显式专用类模板静态数据成员的定义具有有序初始化。其他类模板静态数据成员(即隐式或显式实例化的专用化)具有无序初始化。其他具有静态存储期的非局部变量具有有序初始化。
粗体句子包括所有静态初始化对象,它们不是实例化类的静态数据成员。它们在一个翻译单元内有序:
在一个翻译单元中定义的具有有序初始化的变量应按其在翻译单元中的定义顺序进行初始化。
因此,总结一下:
受静态初始化影响且不是实例化类的静态数据成员的变量将按定义顺序在翻译文件中销毁。
那些变量总是在销毁任何动态初始化对象之后销毁。
但是,尽管可能存在争议性错误,Clang和GCC目前似乎没有以这种方式实现:Demo

这是来自C++14(FD)IS吗? - Kerrek SB
@KerrekSB N4140,据说只包含编辑修改。让我检查一下是否符合最新的草案。 - Columbo
@KerrekSB(或者,提供我应该引用和检查这些引语的文档链接) - Columbo
没错,它在N4140里。奇怪的是,这个粗体短语似乎已经从头脑中消失了(https://github.com/cplusplus/draft/commit/915e0b1c8f30e62e3430a012c2505f0f5469d3b2)。真尴尬,我的整个问题都基于错误的文档。谢谢! - Kerrek SB

2

[basic.start.term]/1 (N4140) 表示:

如果一个对象是静态初始化的,那么它的销毁顺序与动态初始化的顺序相同。

据我理解,这意味着为了确定销毁的顺序,所有静态初始化都被视为动态初始化(有序或无序),并且析构函数按照此初始化的相反顺序调用。


是的,但对于动态初始化,有一组关于顺序(有序 vs 无序)的规则。在这个规则的视角下,静态初始化对象如何适应这个顺序呢? - Kerrek SB
你看,它说“在单个翻译单元中定义有序初始化变量应按其定义顺序进行初始化”。但只有当初始化被有序地执行时才有帮助,而对于静态初始化变量来说并非如此。 - Kerrek SB
@KerrekSB 我觉得我的回答相当清楚了(更新了我的回答),但说实话,我还没有测试过这是否与当前的编译器一起正常工作。 - Anton Savin
@Anton 你引用的内容是从哪里来的?有时候文档很重要。 - user3920237
@remyabel 在N4140中,但在N3337中是相同的。 - Anton Savin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接