std::is_nothrow_move_constructible_v仅检测移动构造函数是否显式声明为noexcept,不检查函数体;vector扩容时依赖它决定用移动还是拷贝路径,误判会破坏强异常安全。

判断一个类型是否能无异常地移动构造,直接关系到 std::vector 等容器在扩容时能否安全调用移动而非拷贝——若误判为 noexcept 移动构造而实际抛出异常,容器扩容可能破坏强异常安全保证,导致数据丢失或未定义行为。
先搞清它到底在测什么
std::is_nothrow_move_constructible
它只看签名:T(T&&) noexcept → 返回 true;T(T&&) → 返回 false;T(T&&) noexcept(false) → 也返回 false。
注意:即使移动构造函数体里一行 throw 都没有,只要没写 noexcept,这个 trait 就是 false。
立即学习“C++免费学习笔记(深入)”;
为什么 vector 扩容时它一票否决
当 std::vector 需要扩容,且元素类型满足 std::is_nothrow_move_constructible_v
这不仅是性能差异:拷贝路径下,若拷贝构造中途抛异常,旧内存仍完好,可维持强异常安全;而移动路径一旦失败,旧对象可能已被移动走(资源已转移),无法恢复原状。
【vector 不会自己验证移动是否真不抛异常,它全信这个 trait】
手动标注 noexcept 的实操步骤
第一步:确认你的类移动构造函数内部不抛异常——包括所有成员的移动构造、基类移动构造、以及所有内联表达式(如 new、访问空指针、除零等静态可判定风险)。
第二步:在移动构造函数声明末尾添加 noexcept 关键字:
class Widget { public: Widget(Widget&& other) noexcept : data_(std::move(other.data_)) { /* 无 new,无 throw,无可能崩溃的操作 */ } private: std::unique_ptr
第三步:用 static_assert 强制校验(推荐加在类定义后):
static_assert(std::is_nothrow_move_constructible_v
若编译失败,说明你标错了——要么函数体里藏了潜在异常点,要么某个成员(比如自定义类型的成员)本身不满足该 trait。
常见踩坑类型与修复方法
方法一:含 std::string 成员的类,默认是 nothrow 移动构造的(C++11 起标准保证),无需额外操作。
方法二:含 std::vector 成员的类,同样默认满足,但前提是其 value_type 本身也满足 std::is_nothrow_move_constructible。
方法三:自定义 RAII 类型,若内部使用 new 分配内存,必须把 new 替换为 new(std::nothrow),否则 operator new 抛异常会导致移动构造非 noexcept:
Resource(Resource&& rhs) noexcept : ptr_(rhs.ptr_) { rhs.ptr_ = nullptr; } // ✅ 安全
Resource(Resource&& rhs) : ptr_(new int(*rhs.ptr_)) { rhs.ptr_ = nullptr; } // ❌ 即使写了 noexcept,new 可能抛 bad_alloc,编译器会忽略该 noexcept 声明


















