![](/img/trans.png)
[英]Potential C++ compiler optimization with/without throw/noexcept function
[英]C++ throw() optimization
根据优化C ++ ,
将空的异常规范(即,将throw()追加到声明中)用于您确定不会抛出异常的函数。
如果我知道我的方法的90%不会抛出异常怎么办? 将throw()附加到所有这些方法似乎是非常规且冗长的。 如果没有,优势是什么? 还是我误会了这里的东西?
C ++ 11引入了noexcept
, throw
稍微弃用(和根据这个效率较低)
noexcept是throw()的改进版本,在C ++ 11中已弃用。 与throw()不同,noexcept不会调用std :: unexpected,并且可能会展开堆栈,也可能不会展开堆栈,这有可能允许编译器在没有throw()的运行时开销的情况下实现noexcept。
当违反空抛出规范时,程序将终止;否则,程序将终止。 这意味着仅当它们具有不引发异常保证时,才应将函数声明为不引发。
最后,您需要一个不抛出noexcept
的move构造函数(用noexcept
指定),以便能够使用std::vector<T>::push_back
的r值引用版本(请参见此处的详细说明)
标准throw()
不会增强可优化性。
如果将一个方法标记为throw()
则编译器将被迫检查是否从该方法引发了异常并展开堆栈-就像该函数未标记为throw()
。 唯一真正的区别是,对于标记为throw()
的函数,当异常离开函数时,将调用全局的unexpected_handler
(通常调用terminate()
),将堆栈展开到该级别,而不是无异常的函数的行为规范将正常处理异常。
对于C ++ 11之前的代码,“ C ++编码标准”中的Sutter和Alexandrescu建议:
避免异常规范。
请特别注意以下规范:不要在函数上编写异常规范,除非您被迫这样做(因为无法更改的其他代码已经引入了它们;请参阅“异常”)。
...
普遍但不正确的信念是,异常规范静态地保证函数将仅抛出列出的异常(可能不会抛出),并基于该知识启用编译器优化
实际上,异常规范实际上所做的只是一些微小的变化,但又有根本的不同:它们使编译器以隐式try / catch块的形式注入附加的运行时开销,以在函数体周围通过运行时检查来强制执行该函数是否确实在执行除非编译器可以静态证明永远不会违反异常规范,否则编译器仅发出列出的异常(可能不发出),在这种情况下可以自由地优化检查。 异常规范既可以启用也可以阻止进一步的编译器优化(除了已经描述的固有开销之外); 例如,某些编译器拒绝内联具有异常规范的函数。
请注意,在某些版本的Microsoft编译器中(我不确定这种行为在最近的版本中是否已更改,但我不认为如此), throw()
以非标准的方式处理。 throw()
等效于__declspec(nothrow)
,它确实允许编译器假定函数不会抛出异常,如果抛出异常,则将导致未定义的行为。
C ++ 11弃用了C ++ 98样式异常规范,并引入了noexcept
关键字。 Bjarne Stroustup的C ++ 11常见问题对此进行了说明:
如果声明为noexcept的函数抛出(从而使异常试图转义,则为noexcept函数),程序将终止(通过调用Terminate())。 终止()的调用不能依赖处于明确定义状态的对象(即,不能保证析构函数已被调用,不能保证堆栈展开,也不能像没有遇到问题一样恢复程序)。 这是经过深思熟虑的,毫无例外地使它成为一种简单,原始且非常有效的机制(比旧的动态throw()机制要有效得多)。
在C ++ 11中,如果从标记为noexcept
的函数引发了异常,则编译器根本没有义务解开堆栈。 这提供了一些优化的可能性。 斯科特·迈耶斯(Scott Meyers)在即将出版的著作《有效的现代C ++》中讨论了新的noexcept
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.