std::is_nothrow_move_constructible 是用于编译期判断类型 T 的移动构造函数是否声明为 noexcept 的类型特征;它只检查公有、可访问、非显式的移动构造函数签名是否含 noexcept,影响标准库容器(如 std::vector)在重分配时选择移动而非拷贝以提升性能。

std::is_nothrow_move_constructible 是什么,什么时候需要它
它是个类型特征(type trait),用来在编译期判断某个类型 T 的移动构造函数是否被声明为 noexcept。不是看它“实际会不会抛异常”,而是看它的声明签名里有没有 noexcept(或 noexcept(true))。这在标准库容器(比如 std::vector)做内存重分配时很关键:如果元素类型支持无异常移动,容器就优先用移动而非拷贝,性能更好;否则退回到拷贝。
常见错误现象是:你写了移动构造函数但没加 noexcept,结果 std::vector::reserve() 或 push_back() 时仍触发拷贝——而你完全没意识到,因为编译器不会报错,运行时也看不出明显问题。
怎么正确检查一个类型的移动构造是否 nothrow
直接用 std::is_nothrow_move_constructible_v<t></t>(C++17 起推荐用 _v 后缀变量模板):
static_assert(std::is_nothrow_move_constructible_v<std::string>, "string must be nothrow move constructible"); static_assert(!std::is_nothrow_move_constructible_v<std::vector<int>>, "vector<int> is not nothrow move constructible (in some lib impls)");
注意:
立即学习“C++免费学习笔记(深入)”;
- 它只检查公有、可访问、非显式(non-explicit)的移动构造函数是否存在且为
noexcept - 如果类型没有移动构造函数(例如只有拷贝),则该 trait 为
false - 继承自基类的移动构造函数不自动继承 noexcept 属性,必须显式标注
- 自定义类型中,即使移动构造函数函数体里什么都没做,也必须手动加上
noexcept,否则 trait 返回false
为什么你的自定义类返回 false,但看起来明明没抛异常
最常见原因是你写了移动构造函数但忘了加 noexcept:
struct MyData {
std::vector<int> data;
// ❌ 错误:没声明 noexcept
MyData(MyData&& other) : data(std::move(other.data)) {}
<pre class="brush:php;toolbar:false;">// ✅ 正确:显式声明
MyData(MyData&& other) noexcept : data(std::move(other.data)) {}};
static_assert(std::is_nothrow_move_constructible_v
其他容易踩的坑:
- 成员变量的移动构造本身不是
noexcept(例如某些老版本 libstdc++ 中std::vector的移动构造未标记noexcept),会导致外层类型连带失败 - 使用了
= default的移动构造函数:C++11 起,编译器生成的默认移动构造函数是否noexcept取决于所有成员是否都满足is_nothrow_move_constructible;但 C++17 起更严格,只要所有成员移动构造都是noexcept,默认版本就自动是noexcept - 类中有 const 成员或引用成员:它们无法被移动,导致编译器无法生成移动构造函数,trait 直接为
false
和 std::is_move_constructible 的区别在哪
std::is_move_constructible 只关心“能否移动构造”(语法上是否可行),不关心异常规范;而 std::is_nothrow_move_constructible 是前者的加强版——它要求不仅可移动,还必须承诺不抛异常。
这意味着:
- 所有
std::is_nothrow_move_constructible_v<t></t>为true的类型,std::is_move_constructible_v<t></t>一定也为true - 但反过来不成立:
std::string在多数实现中是noexcept移动,而std::function通常不是(取决于其内部可调用对象) - 标准容器如
std::vector、std::deque的移动构造是否noexcept是实现定义的,不能假设;必须实测或查文档
真正容易被忽略的是:这个 trait 的结果依赖于整个类型定义链上的每一个移动操作——包括基类、成员、甚至模板参数类型。改一个成员的移动构造声明,可能让整个类的 is_nothrow_move_constructible 状态翻转。


















