std::is_swappable_v仅检测swap(a,b)能否编译,不保证性能、异常安全或实际调用哪个swap;它通过sfinae探测adl可见的swap函数是否合法,与noexcept无关,也不区分移动或拷贝实现。

std::is_swappable_vswap(a, b) 这句能不能过编译,不能帮你选更快的 swap,也不能保证不抛异常 —— 别在 if constexpr 里拿它当优化开关。
std::is_swappable_v 到底检查什么
它本质是 SFINAE 探测:using std::swap; swap(std::declval<t>(), std::declval<t>())</t></t> 在不求值语境下是否合法。这意味着:
- 只要 ADL 找到任意一个
swap函数(哪怕是你自己写的、内部三步拷贝实现的),就返回true - 即使标准库没为
T提供特化,但你在T所在命名空间写了void swap(T&, T&),也会通过 - 若
T是未定义完整的前向声明(如class Foo;),行为未定义 —— GCC 可能静默返回false,Clang 可能直接报错 - 完全不关心该
swap是否noexcept,也不管它用的是移动还是拷贝
为什么 std::is_swappable_v<:vector>> 在老 GCC 上返回 false
GCC 7.5 之前对类模板参数推导和 std::declval 的 ADL 探测支持不全,导致内部探测路径失效。常见现象:
-
std::swap(v1, v2)实际能调用、运行正常 - 但
static_assert(std::is_swappable_v<:vector>>);</:vector>编译失败 - 升级到 GCC 8+ 或 Clang 6+ 即可修复
- 临时 workaround:显式在
std命名空间加swap声明,或改用std::is_swappable_with_v<:vector>&, std::vector<int>&></int></:vector>
std::is_swappable_v 不该用在哪些地方
它不是运行时分支依据,也不是性能判断代理。典型误用场景包括:
- 写
if constexpr (std::is_swappable_v<t>) { std::swap(a, b); }</t>—— 完全多余,std::swap本身就有基于拷贝的兜底实现 - 当成“高效交换”信号:哪怕返回
true,swap 内部仍可能触发两次拷贝构造 + 一次析构 - 替代
std::is_nothrow_swappable_v<t></t>做异常安全判断 —— 后者才真正关联noexcept保证 - 在 requires 子句中单独约束容器元素:应搭配
std::is_move_constructible_v<t></t>和std::is_move_assignable_v<t></t>才更贴近“可高效转移”语义
真正该看的 trait 是哪些
如果你在写泛型容器或资源管理类,关注点往往不是“能不能 swap”,而是“swap 是否安全、高效、可控”:
- 强异常安全场景(如
std::vector::resize):必须查std::is_nothrow_swappable_v<t></t> - 想确保移动语义可用:优先检查
std::is_move_constructible_v<t></t>和std::is_move_assignable_v<t></t> - 要让 ADL 自动选最优 swap:直接写
using std::swap; swap(a, b);,而不是绕路查 trait - 自定义类型想被正确识别:在类同命名空间提供
friend void swap(MyType&, MyType&) noexcept,而非在全局或std里特化
最常被忽略的一点:trait 返回 true 并不意味着你写的 swap 被实际调用了 —— 重载解析和 ADL 规则才是最终决定者,而 std::is_swappable_v 对此毫无影响。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











