std::is_nothrow_swappable 是编译期 trait,仅检查 swap 函数是否显式或隐式声明为 noexcept,不验证实际行为;需为自定义类型提供 adl 可见的 noexcept swap,推荐用 noexcept(noexcept(...)) 推导。

std::is_nothrow_swappable 是编译期判断,不是运行时检测
它只看 swap 函数的 noexcept 说明符是否被显式或隐式标记为 noexcept,不关心实际交换逻辑会不会抛异常。比如你手写了一个没加 noexcept 的 swap,哪怕里面只有一行 std::swap,std::is_nothrow_swappable_v<t></t> 也会是 false。
常见错误现象:static_assert(std::is_nothrow_swappable_v<mytype>, "must be nothrow swappable");</mytype> 突然失败,但你确认成员都支持无异常交换——大概率是漏写了 swap 的 noexcept 声明。
- 必须为自定义类型提供
swap函数,并显式标注noexcept(推荐用noexcept(noexcept(...))形式推导) - 依赖 ADL 的
swap:确保你提供的swap在对应命名空间,且签名正确(通常为void swap(T&, T&) noexcept) - 若未提供自定义
swap,会退回到std::swap,而std::swap的noexcept性取决于其内部调用的移动构造/移动赋值是否noexcept
怎么写一个真正 nothrow 的 swap 函数
光声明 noexcept 不够,得保证函数体里所有操作都不抛异常。C++11 起,std::swap 默认是 noexcept 的,但前提是模板参数满足 std::is_nothrow_move_constructible_v 和 std::is_nothrow_move_assignable_v。
使用场景:实现强异常安全的容器(如 vector::swap)、配合 std::move_if_noexcept、或作为其他 traits(如 std::is_nothrow_move_constructible)的间接依赖。
立即学习“C++免费学习笔记(深入)”;
- 对类成员逐个
swap,每个都要确保其swap是noexcept的;基础类型、std::unique_ptr、std::array等默认满足 - 避免在
swap里调用可能抛异常的操作:比如new、std::string::resize、任何用户自定义函数(除非你 100% 确认它noexcept) - 推荐写法:
void swap(MyType& other) noexcept(noexcept(std::swap(a, other.a)) && noexcept(std::swap(b, other.b))) { std::swap(a, other.a); std::swap(b, other.b); }
std::is_nothrow_swappable_v 为 false 的典型原因
不是“交换会抛异常”,而是“编译器无法证明它不会抛”。这很关键——它影响的是泛型代码的分支选择(比如 std::vector::resize 内部是否敢用移动而非复制),而不是运行时报错。
常见错误现象:明明所有成员都 trivially swappable,但 std::is_nothrow_swappable_v<mystruct></mystruct> 是 false;或者在模板中用了 static_assert 却过不了。
- 类里有用户定义的非
noexcept移动构造函数或移动赋值运算符(即使没显式写throw(),缺省也不是noexcept) - 基类或成员的
swap没提供,或提供了但没noexcept声明 - 使用了
std::tuple或std::pair包含非nothrow类型:它们的swap是noexcept的当且仅当所有元素的swap都是noexcept - 注意:空类、POD 类、只含
int/double成员的类,如果没自定义任何特殊成员函数,std::is_nothrow_swappable_v通常是true(靠std::swap的默认实现)
别把它和 std::is_swappable_v 混用
std::is_swappable_v<t></t> 只检查能不能调用 swap(语法合法),而 std::is_nothrow_swappable_v<t></t> 多了一层语义约束:要求这个 swap 必须是 noexcept 的。两者不互为子集,也不等价。
性能 / 兼容性影响:在 C++17 中二者都是标准 trait;C++14 没有 std::is_nothrow_swappable,需手动模拟(用 noexcept(swap(std::declval<t>(), std::declval<t>()))</t></t>)。
- 泛型库中常同时检查:
if constexpr (std::is_nothrow_swappable_v<t>) { /* fast path */ } else if constexpr (std::is_swappable_v<t>) { /* fallback */ }</t></t> - 别用
try/catch围绕swap来“验证”它是否真的不抛——这既破坏语义,又掩盖了 trait 设计本意:它是给编译器看的契约,不是运行时守卫 - 第三方类型(如
boost::optional)是否满足,得查它的文档或源码;不能假设“只要能 swap 就 nothrow”
最易被忽略的一点:这个 trait 的结果高度依赖你是否提供了正确的 swap 声明,以及是否把 noexcept 推导写完整。它不“智能”,只机械地查符号和属性——写错一个 noexcept,整个链就断了。









