std::is_nothrow_move_assignable 是一个编译期布尔类型特征,用于判断类型 t 是否具有声明为 noexcept 的移动赋值运算符(或退而求其次的 noexcept 拷贝赋值),仅检查声明签名是否含无参 noexcept,不验证运行时行为,要求 t 为完整类型。

std::is_nothrow_move_assignable 是什么,能判断什么
std::is_nothrow_move_assignable 是一个类型特性(type trait),用于在编译期判断某个类型是否支持**不抛异常的移动赋值操作**。它不检查你有没有定义移动赋值运算符,而是检查 T& operator=(T&&) 是否被声明为 noexcept(或隐式满足 noexcept 条件)。如果该操作保证不抛异常,value 为 true;否则为 false。
注意:它只看移动赋值运算符本身的异常规范,不追溯其内部调用的成员函数是否 noexcept —— 除非你显式标记了 noexcept,否则编译器不会自动推导出“安全”。
怎么写才能让 is_nothrow_move_assignable 返回 true
要让 std::is_nothrow_move_assignable_v<t></t> 为 true,关键不是“有移动赋值”,而是“这个移动赋值被标记或推导为 noexcept”。常见方式包括:
- 显式声明为
noexcept:T& operator=(T&&) noexcept { ... } - 使用
= default,且所有成员和基类的移动赋值都满足noexcept(此时编译器会自动推导noexcept) - 不声明移动赋值,但拷贝赋值被删除、且移动构造存在并
noexcept—— 这种情况不触发移动赋值,is_nothrow_move_assignable仍为false(因为没有移动赋值操作)
反例:即使你写了移动赋值,但没加 noexcept,且内部调用了可能抛异常的函数(如 std::vector::push_back),那 std::is_nothrow_move_assignable_v<t></t> 就是 false,哪怕代码能编译通过。
实际检查时容易忽略的细节
检查结果依赖完整类型定义,不能在类定义中途使用(比如在类体内用 static_assert 检查自身)—— 因为此时类还没定义完,std::is_nothrow_move_assignable_v<t></t> 会被当作不完整类型处理,多数标准库实现返回 false。
常见误判场景:
- 成员含
std::string或std::vector:它们的移动赋值是noexcept(C++11 起),所以只要你不干扰,默认支持 - 含自定义类型成员:若该类型移动赋值未标记
noexcept,即使你自己的移动赋值为空,整体也会是false - 继承非
noexcept移动赋值的基类:派生类默认生成的移动赋值会继承基类的异常规范,导致整体不满足
验证方法很简单:
static_assert(std::is_nothrow_move_assignable_v<myclass>, "MyClass must be nothrow move-assignable");</myclass>放在类定义之后、使用之前。
为什么 std::vector 在扩容时关心这个
std::vector 在重新分配内存并迁移元素时,会优先选择移动而非拷贝 —— 但前提是移动操作是 noexcept。如果 std::is_nothrow_move_assignable_v<t></t> 为 false,std::vector 会退回到拷贝赋值(更安全,但更慢),以避免迁移中途异常导致资源泄漏或状态不一致。
这意味着:即使你写了移动赋值,如果没让它 noexcept,std::vector<t></t> 的 push_back、resize 等操作就无法享受移动优化,性能可能差一倍以上。这不是警告,是标准明确规定的回退行为。
真正难的不是写 noexcept,而是确保所有被调用的成员操作也满足它 —— 这个链条一旦断在任意一环,整个类型就掉出 nothrow 移动集合。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











