7

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

§6.7 [stmt.dcl] p4

[...] 否则,此类变量在控制第一次通过其声明时被初始化;这样的变量在其初始化完成时被认为已初始化。如果初始化抛出异常退出,说明初始化未完成,下次控件进入声明时会再次尝试。

没有提到什么可能导致初始化被重试,如果初始化失败除了抛出异常(longjmp(),tad exit,信号等等)。

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

这是标准中的规格不足(并且是这样的缺陷)吗?

4

2 回答 2

2

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

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

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

“信号”也是如此,无论它们是什么。C++ 规范并没有说这些会导致函数退出。这里是龙。

至于longjmp,那是由 的行为所涵盖的longjmp。当您使用longjmp退出函数时,该函数永远不会完成,就像您使用throwand一样catch。而在 C++ 中,对象仅在其构造函数完成时才被构造。因此对象的初始化从未完成,它是未初始化的。

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

§18.10 [support.runtime] p4:

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

所以我认为这并没有被低估。它可能没有很好和整齐地布置,但所有的部分都在那里。

于 2012-01-30T03:29:31.877 回答
0

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

我认为 Nicol 的回答大部分是正确的,但是一个非平凡的构造函数并不意味着一个非平凡的析构函数。longjmp因此可能会中断初始化,因此必须重试。这仅在多线程环境中是棘手的,其中需要互斥锁来防止线程之间争相成为第一个执行初始化的竞争条件。幻象互斥对象需要一个非平凡的析构函数,即使初始化的对象没有析构函数。可能的结果是死锁。这可能是 DR 的好材料。

于 2012-01-30T16:13:07.047 回答