在可见性有限的 `noexcept` 函数中调用 `std::terminate` - gcc vs clang codegen

`std::terminate` invocation in a `noexcept` function with limited visibility - gcc vs clang codegen

考虑以下代码片段:

void f();

void a()          { f(); }
void b() noexcept { f(); }

在上面的场景中,f 的主体在当前翻译单元中对编译器不可见。因此,由于 b 被标记为 noexcept,因此必须在调用方生成额外的代码以确保捕获异常并调用 std::terminate

这就是 clang++ -Ofast -std=c++2a 所做的 (主干版本):

a(): # @a()
  jmp f() # TAILCALL
b(): # @b()
  push rax
  call f()
  pop rax
  ret
  mov rdi, rax
  call __clang_call_terminate
__clang_call_terminate: # @__clang_call_terminate
  push rax
  call __cxa_begin_catch
  call std::terminate()

但是g++ -Ofast -std=c++2a没有(trunk版):

a():
  jmp f()
b():
  jmp f()

live example on godbolt.org


g++ 是如何摆脱困境的?不应该在调用方生成代码,因为 f 的主体不可见吗?

...或者这只是 Compiler Explorer 的怪癖?

正如@ach 回答的那样,有一个 bug opened on gcc bug tracker。但如果我可以这么说,这不是主要问题。

影响是异常会泄漏而不是终止程序但是:

  • 如果你想在未捕获的异常上调用终止,那已经是这样了
  • 如果你想要不抛出异常的安全保证,那不提供。

我唯一能想到的就是在开发的时候。或者在环境中,如果合同被破坏(当生命受到威胁时)必须失败;在这种情况下,编译器和要使用的功能受到严格控制。

A​​ndrzej 在 his article - noexcept — what for ?

中表现出色