c++oding="utf-8" ?>
std::is_nothrow_move_constructible_v决定vector扩容时用移动还是拷贝:为true则优先移动构造,否则退化为拷贝构造;需显式声明noexcept,否则即使= default也不满足,影响性能与强异常安全性。

std::is_nothrow_move_constructible 在 vector 扩容时决定是否能用移动而非拷贝
当 std::vector 需要扩容(比如调用 push_back 触发重新分配),它会把旧元素搬到新内存。搬的方式取决于元素类型是否满足 std::is_nothrow_move_constructible_v<t></t>:如果为 true,优先调用移动构造;否则退回到拷贝构造——哪怕类型有移动构造函数,只要它没标 noexcept,标准就认为“不安全”,不敢在异常敏感路径(如扩容中途)用它。
这直接关系到性能和异常安全性:
- 移动通常比拷贝快一个数量级(尤其含指针或大缓冲区的类)
- 若移动构造可能抛异常,扩容过程无法保证强异常安全:旧内存已释放、新内存构建一半失败 → 对象处于不可恢复状态
- 编译器不会帮你加
noexcept,必须显式声明
怎么查你的类型是否满足 std::is_nothrow_move_constructible
别靠猜,写个最小验证片段:
#include <type_traits>
#include <vector><p>struct Widget {
std::string data;
Widget(Widget&&) = default; // 缺少 noexcept → 不满足
};</p>
<p>static_assert(!std::is_nothrow_move_constructible_v<widget>, "Widget must be nothrow move constructible");</widget></p>
<p>// 修复:
struct WidgetFixed {
std::string data;
WidgetFixed(WidgetFixed&&) noexcept = default; // ✅
};
static_assert(std::is_nothrow_move_constructible_v<widgetfixed>, "");
</widgetfixed></p></vector></type_traits>
注意几个关键点:
-
= default的移动构造函数默认不是noexcept,即使所有成员都满足 -
std::string的移动构造是noexcept(C++11 起),但组合后仍需显式标注 - 用
static_assert卡在编译期,比运行时崩溃早得多
vector 扩容时的移动/拷贝行为差异实测
以下代码在 GCC 13 + -O2 下可观察到明显耗时差:
struct Heavy { char buf[1024]; Heavy(Heavy&&) noexcept { /* trivial */ } };
struct HeavyCopy { char buf[1024]; HeavyCopy(HeavyCopy&&) { /* same body, but no noexcept */ } };
<p>std::vector<heavy> v1;
for (int i = 0; i <p>std::vector<heavycopy> v2;
for (int i = 0; i </heavycopy></p>
<p>原因在于:<code>v2</code> 的元素不满足 <code>std::is_nothrow_move_constructible</code>,<code>vector</code> 内部被迫走拷贝分支(见 libstdc++ <code>__uninitialized_move_if_noexcept_a</code> 实现)。即使你手动写 <code>std::move(x)</code>,底层策略仍由该 trait 控制。</p>
<p>性能影响不是线性的——数据量越大、单次拷贝越重,差距越明显;某些 STL 实现还会因退化为拷贝而多一次内存清零。</p>
<h3>容易被忽略的隐式陷阱:继承与模板推导</h3>
<p>即使你写了 <code>noexcept</code>,也可能被覆盖:</p>
<ul>
<li>派生类默认生成的移动构造函数不继承基类的 <code>noexcept</code> 属性,必须显式写 <code>Derived(Derived&&) noexcept = default;</code>
</li>
<li>模板类中,<code>T</code> 的移动是否 <code>noexcept</code> 取决于具体实例化类型,例如 <code>std::vector<:unique_ptr>></:unique_ptr></code> 满足,但 <code>std::vector<:shared_ptr>></:shared_ptr></code> 在部分旧标准库中不满足(因控制块原子操作可能抛 <code>std::bad_alloc</code>)</li>
<li>聚合类型(如 <code>struct A { int x; std::string s; };</code>)的默认移动构造函数是否 <code>noexcept</code>,取决于所有非静态成员的移动构造是否都 <code>noexcept</code>;<code>std::string</code> 是,但自定义类型容易漏</li>
</ul>
<p>真正卡住的往往不是你自己写的类,而是你用的第三方容器嵌套了一层又一层之后,某处 <code>noexcept</code> 断掉了。</p></heavy></p>C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











