为何C++的std::vector扩容时不采用移动语义?
为什么std::vector扩容时不使用移动构造函数?
核心原因是C++标准要求std::vector扩容时必须保证强异常安全,只有当对象的移动构造函数被标记为noexcept(保证不会抛出异常)时,才会优先使用移动语义;否则会退而求其次使用拷贝构造,避免移动过程中抛出异常导致数据丢失。
强异常安全的约束
当std::vector扩容时,如果操作过程中抛出异常,必须保证原容器内的所有数据完好无损,不能出现旧对象被破坏、新内存对象未构造完成的中间状态:
- 若使用可能抛异常的移动构造:移动操作会修改旧对象的内部状态(比如转移指针所有权),一旦移动到一半抛出异常,旧对象已经被掏空,新内存里的对象也不完整,此时无法恢复原数据,直接违反强异常安全要求。
- 若使用拷贝构造:拷贝操作不会修改旧对象,就算拷贝过程中抛异常,原容器的旧对象依然完好,可以安全回滚。
你的代码问题分析
你定义的object类的移动构造函数没有标记noexcept,标准库会默认认为这个移动构造函数可能抛出异常,因此扩容时选择了更安全的拷贝构造。只需要给移动构造函数加上noexcept标记,就能触发移动语义:
object(object &&) noexcept {std::cout << "Move Construction.\n";}
修改后运行代码,输出就会符合你的预期:
Construction. Construction. ---------------------- Construction. Move Construction. Move Construction. ----------------------
补充说明
对于std::string这类标准库类型,它们的移动构造函数是天然noexcept的,所以在C++11及之后的标准库实现中,std::vector<std::string>扩容时会自动使用移动构造,不会有你担心的效率问题。只有当自定义类型的移动构造无法保证无异常时,才会回到拷贝逻辑。
内容的提问来源于stack exchange,提问作者DarkSharpness
相关产品推荐
相关产品推荐

