C++开发疑问:是否需在多数函数使用noexcept说明符与运算符?
C++标准库中
noexcept的使用疑问 我是C++新手,正在学习基础迭代器/反向迭代器类和标准库容器的实现。观察GCC、MSVC等主流标准库实现时发现,大部分函数会用noexcept说明符和noexcept运算符检测是否抛出异常,比如:
_GLIBCXX17_CONSTEXPR reverse_iterator(const reverse_iterator<_Iter>& __x) _GLIBCXX_NOEXCEPT_IF(noexcept(_Iterator(__x.current))) : current(__x.current) { } _GLIBCXX_NODISCARD _GLIBCXX17_CONSTEXPR iterator_type base() const _GLIBCXX_NOEXCEPT_IF(noexcept(_Iterator(current))) { return current; }
但有些函数却没加,比如反向迭代器的operator==:
template<typename _Iterator> _GLIBCXX_NODISCARD inline _GLIBCXX17_CONSTEXPR bool operator==(const reverse_iterator<_Iterator>& __x, const reverse_iterator<_Iterator>& __y) { return __x.base() == __y.base(); }
我知道noexcept能让编译器做优化,且只有确定函数(及内部调用函数)不会抛异常时才该用。我理解标准库中这么做是因为涉及模板,需要检测迭代器类型的操作是否会抛异常(如果理解错了请纠正)。现在有三个疑问:
- 哪些场景下需要用
noexcept说明符和运算符检测无异常? - 上面示例里的比较运算符为什么没加
noexcept? - 能不能给这类比较运算符标记
noexcept,或者检测它是否是noexcept的?
问题解答
1. 需使用noexcept检测的场景
- 模板适配多类型需求:标准库的容器、迭代器都是模板,要兼容各种用户自定义类型。比如反向迭代器的拷贝构造函数,必须根据底层迭代器
_Iterator的构造是否noexcept,来决定自身的异常特性,这时候就得用noexcept(...)运算符做条件判断,保证模板行为和底层类型匹配。 - 符合C++标准强制要求:C++标准对很多库函数的异常规格有明确规定,比如容器的移动构造、swap操作,要求在可行的情况下必须是
noexcept的,标准库实现必须通过noexcept运算符适配不同类型,满足标准的异常保证。 - 性能敏感的高频操作:像迭代器的
base()这类频繁调用的小函数,或者内存分配相关操作,标记noexcept能让编译器省去异常处理的额外开销,生成更高效的机器码。 - 强异常保证的操作:如果某个操作需要保证"要么完全成功,要么完全不执行",标记
noexcept后,一旦函数抛出异常程序会直接终止,不会留下无效的中间状态。
2. 比较运算符未用noexcept的原因
你看到的GCC实现没加,主要是标准版本迭代的历史遗留:
- C++17之前,标准并没有强制要求迭代器的
operator==必须是noexcept的,早期的标准库实现也就没添加相关说明。 - GCC的
_GLIBCXX_NOEXCEPT_IF是对应C11条件noexcept的宏,部分操作符可能因为版本兼容策略,没有及时补上。不过C20之后,标准明确要求所有迭代器的相等比较必须是noexcept的,新版GCC标准库已经补上了这个noexcept说明。
另外还有个潜在原因:早期标准没强制迭代器的operator==不能抛异常,所以实现上也就没加条件noexcept来适配底层迭代器的异常特性。
3. 能否给比较运算符加noexcept或检测?
完全可以,这也是现代C++的推荐做法:
- 标记条件
noexcept:可以用noexcept运算符检测底层迭代器的operator==是否noexcept,然后给反向迭代器的比较运算符加上条件说明,示例如下:
这样就能让反向迭代器比较运算符的异常特性和底层迭代器保持一致。template<typename _Iterator> _GLIBCXX_NODISCARD inline _GLIBCXX17_CONSTEXPR bool operator==(const reverse_iterator<_Iterator>& __x, const reverse_iterator<_Iterator>& __y) noexcept(noexcept(__x.base() == __y.base())) { return __x.base() == __y.base(); } - 检测是否
noexcept:可以用noexcept(expr)运算符直接判断,或者使用C++17引入的std::is_nothrow_equal_to类型特性,比如:static_assert(noexcept(std::declval<reverse_iterator<_Iter>>() == std::declval<reverse_iterator<_Iter>>()), "operator== must be noexcept");
内容的提问来源于stack exchange,提问作者user11611653
相关产品推荐
相关产品推荐

