std::is_swappable_v仅检测swap(a,b)语法是否合法,不保证性能、异常安全或影响std::swap行为;它通过SFINAE探测ADL找到的swap函数是否可用,与移动操作无关。

std::is_swappable_vswap(a, b) 这句语法能不能过编译,不保证它快、不抛异常、也不影响 std::swap 的实际行为 —— 别拿它当性能开关。
std::is_swappable_v 到底检查什么
它本质是 SFINAE 式探测:using std::swap; swap(std::declval<t>(), std::declval<t>())</t></t> 在不求值语境下是否合法。
这意味着:
- 只要 ADL 找到一个
swap函数(哪怕是你自己写的、内部用拷贝实现的),就返回true - 即使标准库没为
T提供特化,但你写了namespace N { void swap(X&, X&) { /* 拷贝三步走 */ } },也会通过 - 如果
T是未定义完整类型的前向声明(比如class Foo;),std::is_swappable_v<foo></foo>行为未定义 —— 编译器可能静默返回false或直接报错 - 它不关心
swap是否noexcept,也不管移动构造/赋值是否存在或高效
为什么 std::is_swappable_v<:vector>> 在老 GCC 上可能返回 false
GCC 7.5 之前对类模板参数推导支持不全,导致 std::is_swappable 内部依赖的 std::declval 和 ADL 探测在某些模板实例化路径下失效。
立即学习“C++免费学习笔记(深入)”;
常见表现:
- 代码里明明能写
std::swap(v1, v2)且正常运行 - 但
static_assert(std::is_swappable_v<:vector>>);</:vector>却失败 - 升级到 GCC 8+ 或 Clang 6+ 即可修复
- 临时 workaround:显式提供
swap声明,或改用std::is_swappable_with_v<:vector>&, std::vector<int>&></int></:vector>
std::is_nothrow_swappable_v 才和异常安全强相关
如果你真需要交换不抛异常(比如在容器 resize、异常安全 swap-and-swap-out 场景),该看的是 std::is_nothrow_swappable_v<t></t>,不是 std::is_swappable_v<t></t>。
它的判定逻辑更严:
- 不仅要求
swap(a, b)语法合法 - 还要求整个表达式被
noexcept修饰,或所有参与调用的函数都声明为noexcept - 等价于
noexcept(swap(std::declval<t>(), std::declval<t>()))</t></t> - 注意:它不等价于 “
T的移动操作是noexcept”,因为swap实现可能绕过移动,比如用拷贝 + 析构
真正该怎么做:别查 trait,直接用 ADL
模板里要安全交换两个对象?别先查 std::is_swappable_v<t></t>,直接写:
using std::swap; swap(a, b);
理由很实在:
- 编译器会自动找最优
swap:先 ADL,再退到std::swap兜底 -
std::swap本身就有针对std::array、std::pair等的特化,效率有保障 - 查
is_swappable再决定是否 swap,属于多一层无意义的编译期分支,不提升性能,反而增加模板膨胀 - 唯一合理使用场景:concepts 约束(如
template<swappable_element t></swappable_element>)或 SFINAE 替代重载,而非运行逻辑分支
最常被忽略的一点:可交换性(Swappable)是语言标准定义的**概念**,不是性能指标。它只关乎语法可行性;而“交换是否高效”“是否无异常”“是否触发移动”,得靠人读源码、看文档、测 profile —— 编译器不会替你做这个判断。


















