- 我知道有很多函数永远不会抛出的示例,但是编译器无法自行确定。在所有这种情况下,我都应该在函数声明中附加noexcept吗?
noexcept
这很棘手,因为它是功能接口的一部分。尤其是,如果您正在编写库,则客户端代码可以取决于noexcept
属性。以后很难更改它,因为您可能会破坏现有代码。当您实现仅由应用程序使用的代码时,可能不必担心。
如果您有一个无法抛出的函数,请问自己是希望留下noexcept
还是会限制将来的实现?例如,您可能想通过抛出异常来引入对非法参数的错误检查(例如,用于单元测试),或者您可能依赖于可能更改其异常规范的其他库代码。在那种情况下,保守和省略是比较安全的noexcept
。
另一方面,如果您确信该函数永远不会抛出并且它是规范的一部分是正确的,则应该声明它noexcept
。但是,请记住,编译器将无法检测到noexcept
您的实现是否发生更改的情况。
- 在哪些情况下应更谨慎地使用noexcept,对于哪种情况我可以摆脱隐含的noexcept(false)?
您应该专注于四类功能,因为它们可能会产生最大的影响:
- 移动操作(移动赋值运算符和移动构造函数)
- 调换操作
- 内存解除分配器(运算符删除,运算符delete [])
- 析构函数(尽管
noexcept(true)
除非隐式创建,否则它们是隐式的noexcept(false)
)
这些函数通常应为noexcept
,并且库实现很可能可以利用该noexcept
属性。例如,std::vector
可以在不牺牲强大的异常保证的情况下使用非投掷移动操作。否则,它将不得不退回到复制元素(就像在C ++ 98中一样)。
这种优化在算法级别上,不依赖于编译器优化。这可能会产生重大影响,尤其是在复制元素昂贵的情况下。
- 使用noexcept之后,我何时可以实际期望获得性能改善?特别是,给出一个示例代码,在添加noexcept之后,C ++编译器能够为其生成更好的机器代码。
noexcept
反对没有异常规范的好处throw()
是,或在栈展开时,该标准允许编译器具有更大的自由度。即使在这种throw()
情况下,编译器也必须完全解开堆栈(并且必须按照与对象构造完全相反的顺序进行操作)。
noexcept
另一方面,在这种情况下,不需要这样做。不要求必须解开堆栈(但仍然允许编译器执行此操作)。这种自由度允许进一步的代码优化,因为它降低了始终能够展开堆栈的开销。
有关noexcept,堆栈展开和性能的相关问题,将在需要堆栈展开时提供有关开销的更多详细信息。
我还建议斯科特·迈耶斯(Scott Meyers)着书“有效的现代C ++”,“第14项:声明函数,除非它们不会发出异常”,以供进一步阅读。
move_if_nothrow
如果有noexcept move ctor ,则使用(或whatchamacallit)的代码将获得性能改进。