是块作用域静态或线程存储持续时间变量初始化失败的原因



在回答了这个问题,但在标准论文中没有找到令人满意的答案后,我开始怀疑。该标准规定了上述变量的以下w.r.t.初始化:

§6.7 [stmt.dcl] p4

[…]否则,在控件第一次通过其声明时初始化这样的变量这样的变量在初始化完成时被视为已初始化。如果初始化通过抛出异常退出,则初始化未完成,因此下次控件进入声明时将重试。

如果初始化失败,除了抛出异常(longjmp(),即退出,仅举几个信号)之外,没有提及可能导致初始化重试的原因。

我是否忽略了标准中的任何内容?我一遍又一遍地查看初始化、声明和异常子句,甚至通过快速搜索"static"来查阅CWG缺陷目录,但找不到任何相关的内容。

这是标准中的规格不足(也是缺陷)吗

C++规范只能定义C++规范中包含的内容。记住:C++规范定义了它定义的虚拟机的行为。如果它没有定义某些事情可以发生,那么它肯定不会定义C++围绕它没有说的事情可以发生的行为。

根据C++规范,线程可以通过三种方式退出:从其主函数返回,通过其主函数抛出异常,以及直接退出进程(与std::terminate或类似函数一样)。简而言之,C++线程不能以任何其他方式退出。标准C++中没有ExitThread函数。类似地,std::thread不能从外部或内部杀死线程。

因此,根据定义,任何导致C++所说的不可能发生的事情都是未定义的。我想这甚至不会是"未定义的行为";在C++11真正确定线程交互的工作方式之前,线程就是在这个模糊的空间里。

"信号"也是如此,不管它们是什么。C++规范没有说明这些会导致函数退出。这是龙。

至于longjmp,它包含在longjmp的行为中。当您使用longjmp退出一个函数时,该函数永远不会结束,就像您使用了throwcatch一样。在C++中,只有当构造函数完成时,才会构造对象。因此,对象的初始化从未完成,并且它未初始化。

我没有ISO C规范(C++引用了longjmp的行为),但C++11强烈建议您可以将throw/catchlongjmp/setjmp等同起来,只要您得到未定义的行为:

§18.10 [support.runtime] p4:

函数签名longjmp(jmp_buf-jbuf,int val)在本国际标准中具有更严格的行为限制。如果用catch和throw替换setjmp和longjmp会为任何自动对象调用任何非平凡的析构函数,那么setjmp/longjmp调用对具有未定义的行为。

所以我不认为这是未明确说明的。它的布局可能不太美观,但所有的部分都在那里。

仅仅因为文本提到了一个特定的案例并不意味着其他案例会有所不同。如果有其他方法可以阻止初始化完成,则实现必须在下一次执行时重试。

我认为Nicol的答案基本上是正确的,但非平凡构造函数并不意味着非平凡析构函数。CCD_ 16因此可以中断初始化,使得它必须被重试。只有在多线程环境中,这才是棘手的问题,因为多线程环境需要一个互斥锁来防止竞争第一个执行初始化的线程之间出现竞争条件。幻影互斥对象需要一个非平凡的析构函数,即使初始化的对象没有析构函数。可能的结果是陷入僵局。这可能是DR的好材料。

最新更新