c++oding="utf-8" ?>
std::vector扩容时仅当std::is_nothrow_move_constructible_v为true才使用移动构造,否则回退拷贝以保证异常安全;需显式声明noexcept且所有成员/基类移动构造也须noexcept,并用static_assert验证。

它不保证“性能安全”,只决定 std::vector 扩容时用移动还是拷贝——而这个选择直接导致 3 倍以上耗时差异和内存峰值翻倍。你写了个 MyType(MyType&&),但没加 noexcept,std::vector::push_back 就永远不会用移动。
为什么 vector 不用你的移动构造函数
标准库不是“看到移动构造就用”,而是查 std::is_nothrow_move_constructible_v<t></t>:为 true 才走移动分支;否则 fallback 到拷贝。这不是优化开关,是异常安全硬约束。
-
std::vector扩容分三步:分配新内存 → 构造新元素 → 销毁旧元素 - 如果移动构造抛异常(比如内部
new失败),旧对象可能已被掏空,无法安全析构 → 状态损坏 - 拷贝则不同:新元素构造失败,旧容器完好无损,可继续使用
- 所以,
std::is_nothrow_move_constructible_v<t></t>为false时,vector宁可慢,也要保状态
怎么让自己的类真正满足 nothrow 移动
光写 MyType(MyType&& other) 不够,必须显式加 noexcept,且所有成员、基类的移动构造也得是 noexcept。
- 写法只能是
MyType(MyType&&) noexcept,不能是noexcept(true)或空括号 - 成员如
std::string、std::vector<int></int>在 C++11 起是noexcept,但std::vector<:string></:string>在某些 libstdc++ 版本中不是 - 含
std::mutex、std::ofstream或自定义资源管理类的类型,基本无法满足——它们的移动可能抛异常或根本不可移动 - 继承类不会自动继承基类的
noexcept移动构造,必须手动声明:Derived(Derived&&) noexcept : Base(std::move(other))
如何验证和防止误用
别靠猜,用 static_assert 在编译期卡住,否则问题只在压测时暴露。
- 在类定义后立即加断言:
static_assert(std::is_nothrow_move_constructible_v<mytype>, "MyType must be nothrow move constructible");</mytype> - 在模板容器里做 SFINAE 分支:
if constexpr (std::is_nothrow_move_constructible_v<t>) { /* move */ } else { /* copy */ }</t> - 启用编译器警告:
-Wnoexcept-type(Clang/GCC)能捕获“声明了noexcept但函数体里写了throw”这种矛盾 - 注意:
std::is_nothrow_move_constructible_v<t></t>返回true≠ 移动构造真的不抛异常,只是你签了契约;若违约,vector扩容时崩溃或未定义行为
最常被忽略的是成员类型的“传染性”:一个 noexcept 移动构造函数,只要调用了某个非 noexcept 的成员移动,整个类型就失效。检查不能只看自己写的那行,得顺藤摸瓜到每个成员的 std::is_nothrow_move_constructible_v 值。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











