std::vector扩容时优先调用移动构造函数,但前提是元素类型满足std::is_nothrow_move_constructible_v且移动赋值也无异常,否则降级为拷贝构造,性能可能显著下降。

它直接决定 std::vector 扩容时用移动还是拷贝——不满足就降级,性能可能腰斩。
std::vector 扩容时到底调不调用移动构造?
当 std::vector 容量不足、需要重新分配内存并迁移元素时,标准要求:若元素类型满足 std::is_nothrow_move_constructible_v<t></t> 且移动赋值也无异常,则优先使用移动;否则退回到拷贝构造(即使你写了移动构造函数)。
- 移动比拷贝快得多(尤其对
std::string、std::vector等管理堆内存的类型) - 但前提是移动构造函数被标记为
noexcept,否则编译器不敢在异常安全前提下“冒险”移动 - 检查方式:
static_assert(std::is_nothrow_move_constructible_v<MyClass>, "MyClass must be nothrow move constructible");
为什么加了 noexcept 还不被识别?
常见误判不是语法问题,而是隐式约束未满足:
- 类中所有成员的移动构造函数也必须是
noexcept(包括基类、组合对象) - 若成员是
std::vector<T>,那T本身也得满足std::is_nothrow_move_constructible_v - 自定义移动构造函数写了
noexcept,但内部调用了可能抛异常的函数(比如没加noexcept的成员函数),会导致整个函数不被视为noexcept - 使用
= default生成移动构造时,编译器会自动推导noexcept性质——但只要任一成员移动构造非noexcept,推导结果就是false
std::deque 和 std::list 也依赖它吗?
依赖程度低,但仍有影响:
立即学习“C++免费学习笔记(深入)”;
-
std::deque内部按块存储,扩容不涉及整体搬迁,所以不依赖该 trait 做移动/拷贝决策;但某些实现中,块内元素重排或迭代器失效恢复时仍可能受益于无异常移动 -
std::list插入/拼接节点指针,几乎不构造新元素,该 trait 几乎无影响 - 真正敏感的是连续内存容器:
std::vector、std::basic_string、std::array(后者仅在作为元素嵌套时)
别只盯着自己写的移动构造函数加 noexcept——整条调用链上的每个移动操作都得稳住,否则 std::vector 就默默切回拷贝模式,你还在纳闷为什么压测时 memcpy 占了 40% CPU。



















