C++14中std::vector移动赋值:setDataVector能否声明为noexcept?
问题解答:C++14中
setDataVector能否标记为noexcept? 1. 直接结论:C++14中不能安全声明为noexcept
在C++14标准下,std::vector<Type>的移动赋值运算符不具备noexcept保证——哪怕Type是uint32_t这种平凡数据类型。因此你的setDataVector函数如果直接依赖vector的移动赋值,不能贸然加上noexcept:一旦标准库实现的移动赋值抛出异常,程序会直接触发std::terminate导致崩溃。
2. 为什么平凡类型的vector移动赋值也可能抛异常?
你可能疑惑:移动vector不就是交换内部指针、大小成员吗?怎么会抛异常?核心原因在于C++17之前的标准没有强制要求vector的移动赋值必须是noexcept,标准库实现存在特殊场景的退避逻辑:
- 多数情况下,移动赋值确实是轻量的指针交换,不会抛异常;
- 但如果目标vector的内存分配器和源vector的分配器不相等(且分配器不满足
propagate_on_container_move_assignment特性),此时移动赋值无法直接交换内部资源,只能退化为深拷贝——而深拷贝可能因内存分配失败抛出std::bad_alloc异常。
哪怕Type是uint32_t,只要分配器不匹配且不支持传播,移动赋值就可能触发拷贝进而抛出异常。
3. 为什么vector、string的移动赋值在C++17前没标记noexcept?
这是标准制定的权衡结果,核心要点有两个:
- 分配器兼容性:早期C++标准对分配器的传播规则没有严格约束,为了兼容不支持移动传播的分配器,标准不能强制移动赋值为
noexcept——否则当分配器不匹配时,实现无法处理拷贝抛出的异常,违反异常安全承诺。 - 异常安全的分层设计:C++的异常安全要求是“不泄漏资源、不破坏对象状态”,移动操作的异常安全指若抛出异常,源对象和目标对象都处于有效状态,而非“绝对不抛出”。早期标准优先保证状态安全,而非强制
noexcept。
C++17开始,标准强制std::vector的移动赋值运算符在分配器满足propagate_on_container_move_assignment或分配器相等时必须是noexcept,这才彻底解决了该问题。
4. C++14中实现条件性noexcept的方案
可以手动判断分配器特性,为函数添加条件性的noexcept保证:
template<typename Type> class Data { private: std::vector<Type> data; public: void setDataVector(std::vector<Type> input) noexcept( std::allocator_traits<typename decltype(data)::allocator_type>::propagate_on_container_move_assignment::value || std::is_nothrow_move_assignable<decltype(data)>::value ) { data = std::move(input); } };
这样只有在移动赋值确实不会抛出异常的场景下,函数才会带上noexcept保证。
内容的提问来源于stack exchange,提问作者DarkLight
相关产品推荐
相关产品推荐

