std::vector扩容时若元素类型不满足std::is_nothrow_move_constructible_v,则退回到拷贝构造以保障强异常安全,即使已定义移动构造函数;因标准要求移动构造必须noexcept才启用移动语义,否则触发o(n)深拷贝,性能显著下降。

std::is_nothrow_move_constructible 影响 vector 扩容时是否触发深拷贝
当 std::vector 容量不足需要扩容时,如果元素类型不满足 std::is_nothrow_move_constructible_v<t></t>,标准库必须退回到拷贝构造(即使你写了移动构造函数),因为移动操作可能抛异常——而 realloc-style 重定位要求强异常安全:要么全成功,要么原容器不变。
这意味着本可 O(1) 移动的 std::string 或自定义类,若移动构造函数没加 noexcept,扩容时会变成 O(n) 拷贝,性能直接腰斩。
为什么加了 noexcept 才算“nothrow”移动构造
std::is_nothrow_move_constructible 不看函数名,只看实际声明签名是否带 noexcept 或 noexcept(true)。即使移动构造函数内部绝不会抛异常,只要没显式标注,编译器就按可能抛处理。
-
T(T&&) noexcept→ 满足 -
T(T&&) noexcept(true)→ 满足 -
T(T&&)(无 noexcept)→ 不满足,哪怕函数体是空的 -
T(T&&) noexcept(false)→ 明确不满足
注意:如果移动构造函数调用了其他可能抛异常的函数(比如某成员的构造),noexcept 会导致编译期或运行期调用 std::terminate,所以务必确保整个链路真正无异常路径。
实测对比:vector vs vector
假设 HeavyClass 有 1MB 堆内存,移动构造逻辑正确但漏写 noexcept:
std::vector<heavyclass> v;
v.reserve(1000);
for (int i = 0; i
<p>此时若 <code>HeavyClass(T&&)</code> 未 noexcept,<code>v</code> 扩容会逐个拷贝(分配新内存 + 复制 1MB × n),而非移动(仅交换指针)。用 <code>valgrind --tool=massif</code> 或 perf 可明显看到内存分配次数和耗时翻倍。</p><div class="aritcle_card flexRow artxards">
<div class="artcardd flexRow">
<a class="aritcle_card_img" rel="nofollow" href="/xiazai/skill5502" title="C++ Code Review Master"><img
src="https://img.php.cn/upload/skill/000/000/081/179051228971575.jpg" alt="C++ Code Review Master" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a rel="nofollow" href="/xiazai/skill5502" title="C++ Code Review Master" class="overflowclass">C++ Code Review Master</a>
<p class="overflowclass">组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。</p>
</div>
<a rel="nofollow" href="/xiazai/skill5502" title="C++ Code Review Master" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
</a>
</div>
</div>
<p>修复只需一行:<br><code>HeavyClass(HeavyClass&& other) noexcept : data_(other.data_) { other.data_ = nullptr; }</code></p>
<h3>别只盯着 move 构造——析构函数也得 noexcept</h3>
<p>某些容器(如 <code>std::deque</code>、<code>std::list</code>)在异常发生时需回滚,也会检查 <code>std::is_nothrow_destructible_v<t></t></code>。虽然 vector 扩容不依赖它,但若你用的是自定义分配器或嵌套在异常敏感上下文中,漏掉析构的 <code>noexcept</code> 仍可能被 SFINAE 或 trait 推导拦截,导致编译失败或静默降级。</p>
<p>建议统一为:<br><code>~HeavyClass() noexcept = default;</code> 或显式写 <code>noexcept</code>。</p>
<p>真正容易被忽略的是:模板类中依赖的成员类型,其 noexcept 移动性由最弱的那个决定——一个 <code>std::vector</code> 成员没问题,但混进一个第三方库的非 noexcept 类型,整个类就“中毒”了。</p></heavyclass>C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










