【问题标题】:Is what constitutes a failed initialization of block-scope static or thread storage duration variables underspecified?是什么构成了块范围静态或线程存储持续时间变量的初始化失败?
【发布时间】:2012-01-30 00:47:00
【问题描述】:

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

§6.7 [stmt.dcl] p4

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

没有提到如果初始化失败除了抛出异常(longjmp()、thead 退出、仅举几例)之外的任何其他原因导致初始化被重试的原因。

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

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

【问题讨论】:

  • C++ 是否定义了与信号相关的任何东西?还是longjmp?线程退出可能会改变一些事情。
  • 您是否有任何具体场景没有被标准充分指定?
  • @Kerrek:链接的问题。 :P
  • @Lightness:setjmp/longjmp 为 18.10/4。另请参阅:open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#28
  • @Xeo:嗯...我不太明白这个问题有什么含糊之处。您能否详细说明为什么您认为该行为没有得到充分明确的规定?

标签: c++ c++11 language-lawyer static-initialization


【解决方案1】:

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/catch 等同于 longjmp/@ 987654333@,当你得到未定义的行为时:

§18.10 [support.runtime] p4:

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

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

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

    • 标准并不是只列出一种情况,它只是指定了重试正是针对这种情况。它没有说“如果失败,下次重试”。它特别说“如果通过抛出失败,下次重试”。
    • @Xeo:一点也不。如果初始化没有完成,那一开始就不是初始化。重试第一次。前面的文字已经暗示这是语言所揭示的,“初始化没有完成,所以会再试一次……”
    • 不,它没有,因为 that 之前的文本只提到了例外情况。你不能只分离两个部分。无论如何,我认为这是一个至少澄清的好候选人。顺便说一句,关于互斥锁问题,我认为第 138 页上的脚注可能适用:“88)实现不得在初始化程序的执行过程中引入任何死锁。”
    • @Xeo:那句话中我省略的部分当然是指例外情况。 that 之前的文本没有提到异常。重试初始化的行为完全由“否则这样的变量在控件第一次通过其声明时被初始化;这样的变量在其初始化完成时被认为已初始化。”换句话说,只通过初始化的中途没有任何意义,第二次仍然算作第一次。此外,“尝试”的概念仅由“再次尝试”引入。
    猜你喜欢
    • 2021-08-20
    • 1970-01-01
    • 2020-02-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-21
    • 2019-02-11
    • 2017-05-26
    相关资源
    最近更新 更多