如果在全局对象的析构函数中调用“std::exit”会发生什么?

yep*_*ons 21 c++ destructor terminate language-lawyer

考虑以下代码:

\n
#include <cstdlib>\nstruct Foo {\n    ~Foo() {\n        std::exit(0);\n    }\n} foo;\nint main() {\n}\n
Run Code Online (Sandbox Code Playgroud)\n

它在我的 Linux(GCC、Clang)和 Windows(Visual Studio)上都成功编译并以零终止。然而,当在Windows()上用MSYS2的GCC编译时g++ (Rev2, Built by MSYS2 project) 10.3.0,它会进入无限递归并因堆栈溢出而死亡。这可以通过在之前添加一些调试输出来检查std::exit();我最初没有添加它是为了避免考虑破坏std::cout.

\n

是否有任何 C++ 标准对这种行为有任何规定?它是否定义良好/实现定义/未定义/等等,为什么?

\n

例如,最近的一些草案对\ 的行为[support.start.term]/9.1进行了以下说明:std::exit

\n
\n

首先,销毁与当前线程关联的具有线程存储持续时间的对象。\n接下来,销毁具有静态存储持续时间的对象,并调用通过调用 atexit 注册的函数。有关销毁和调用的顺序,请参阅 [basic.start.term]。

\n
\n

[basic.start.term]/1我猜它指的是:

\n
\n

具有静态存储持续时间的构造对象([dcl.init])将被销毁,并且在 std\xe2\x80\x8b::\xe2\x80\x8batexit 中注册的函数将作为 std\xe2\x80\x8b 调用的一部分被调用: :\xe2\x80\x8bexit ([support.start.term])。\n对 std\xe2\x80\x8b::\xe2\x80\x8bexit 的调用在销毁和注册函数之前排序。

\n
\n

我没有看到对std::exit析构函数调用有任何直接限制。

\n

旁注:请不要评论“这段代码很糟糕”、“你不应该在全局对象中使用析构函数”(你可能不应该)并在评论中探究 XY 问题。将此视为一个好奇的学生提出的学术问题,他知道对原始问题有更好的解决方案,但在探索 C++ 的广阔领域时偶然发现了这个怪癖。

\n

Lan*_*yer 43

[基本.start.main]/4 :

\n
\n

如果std\xe2\x80\x8b::\xe2\x80\x8bexit在销毁具有静态或线程存储持续时间的对象期间调用来结束程序,则该程序具有未定义的行为。

\n
\n

  • @LanguageLawyer 没有。只是标准中定义了这种特殊的边缘情况 (5认同)
  • “_结束程序_”部分似乎有点多余。:) (3认同)