g++ -fno-enforce-eh-specs - 为啥/如何违反 C++ 标准?

Posted

技术标签:

【中文标题】g++ -fno-enforce-eh-specs - 为啥/如何违反 C++ 标准?【英文标题】:g++ -fno-enforce-eh-specs - why/how does this violate the C++ standard?g++ -fno-enforce-eh-specs - 为什么/如何违反 C++ 标准? 【发布时间】:2016-12-25 22:09:27 【问题描述】:

来自man gcc

  -fno-enforce-eh-specs
      Don't generate code to check for violation of exception specifications
      at run time.  This option violates the C++ standard, but may be useful
      for reducing code size in production builds.

当编译器进行优化时,它会消除各种检查,打破函数之间的界限,甚至可能避免开发人员在某些情况下放入代码中的系统调用之类的事情。所以:

为什么异常规范违规检查如此特殊以至于不能跳过? 具体检查的是什么? 如果使用此开关对于减少代码大小非常有用,那么 C++ 标准为什么需要这些检查?我认为这个想法是尽可能实现零开销抽象。

【问题讨论】:

只要可观察的行为不改变,优化就可以做任何他们想做的事情。放弃对异常规范的检查确实会改变行为,因为被抛出的错误异常被强制调用std::terminate(),如果不检查异常类型但实际上抛出了错误的异常is,则不会发生这种情况. @DietmarKühl:如果函数实际上没有抛出任何禁止的异常,它不会改变......但我想我明白你的意思了。 【参考方案1】:

(现在是旧的?)标准要求声明为void f() throw(x); 的函数不得抛出除x 以外的任何异常(或可能源自x?)。如果它试图抛出其他东西,应该调用std::unexpected(),并且可能最终调用std::terminate() 来杀死程序。

但如果在编译时无法确定异常的确切类型,则可能需要运行时检查以确定异常是否属于可接受的类型。

当然,如果该选项被该选项移除,一个不正确的异常可能会从函数中逃脱。那将是违反标准的。

【讨论】:

所以你是说这个限制与 C++17 无关? 同样的规则仍然适用于noexcept:如果noexcept函数抛出异常,标准要求类似处理;这也需要代码可能调用std::terminate(而不是std::unexpected)。并且仍然非常推荐noexcept。代码成本可能比throw(x) 小,因为不涉及异常类型检查,但成本仍然不为零。【参考方案2】:

为什么异常规范违规检查如此特殊以至于不能跳过? 究竟要检查什么?

这已经回答过了,但为了我的回答也完整:对于具有throw(...)noexcept 规范的函数,当抛出不允许的异常时,必须调用std::unexpected()std::terminate()

实际上,它们变成了类似的东西

void f();
void g() noexcept  f(); 

变成非常相似的东西:

void f();
void g()  try  f();  catch (...)  std::terminate();  

如果使用此开关对于减少代码大小非常有用,那么 C++ 标准为什么需要这些检查?我认为这个想法是尽可能零开销的抽象。

确实如此。但是没有开销就没有办法实现这一点。编译器确实已经设法以这样一种方式实现异常,即在运行时执行的代码,只要没有抛出异常,就与没有异常处理的版本相同。从这个意义上说,在具有合适 ABI 的平台上,已经实现了零开销异常。然而,他们仍然使用特殊标记来注释该代码,以便运行时库确定最终抛出异常时要调用的代码。那些特殊的标记占用空间。最终解释这些标记的代码也占用了空间。而实际的异常处理代码,即使只有std::terminate(),也占用了更多的空间。

【讨论】:

以上是关于g++ -fno-enforce-eh-specs - 为啥/如何违反 C++ 标准?的主要内容,如果未能解决你的问题,请参考以下文章

编码g.711 g.168 g.729 g.723 哪个好

二手iPad 价格

g:formatDate 作为 g:textField 上的值

g++、clang++、使用libboost的编译花絮——g++7成功时g++8编译失败;

g++ 已安装但 make 说 g++ 未找到(奇怪)

g是代表啥单位