std::is_swappable 是编译期类型特征,仅检测 swap 调用语法合法性,不保证高效性或异常安全性,也不影响编译器优化决策,典型用途是 sfinae 或 concepts 约束。

std::is_swappable 是什么,它真能帮你优化 swap 吗?
不能。它只告诉你类型是否支持 swap(即有没有合法的 swap 调用),但不保证这个 swap 是高效的、非拷贝的,也不参与编译器优化决策。你写 std::swap(a, b) 时,编译器根本不会看 std::is_swappable_v<t></t> 的值——它只按重载解析和 ADL 找实际调用的函数。
它的典型用途是 SFINAE 或 requires 约束中做编译期检查,比如写一个泛型容器时,想确保元素类型能被安全交换:
template <typename T> concept swappable_element = std::is_swappable_v<T>;
怎么正确检测“可交换且高效”?
没有标准 trait 能直接告诉你某个 swap 是否“高效”。所谓高效,通常指:不触发拷贝构造/赋值、不抛异常、复杂度为 O(1)。这得靠人来判断,不是编译器能自动推的。
-
std::is_swappable_v<t></t>为true只说明swap(t1, t2)语法合法 —— 它可能内部调用了拷贝构造 + 移动赋值,甚至抛异常 - 真正高效的
swap一般来自:std::swap对内置类型/POD 的特化、std::vector等标准容器的自定义swap成员函数、或你手动为自定义类型提供的非成员swap(声明在类同命名空间,供 ADL 找到) - 若你写了
friend void swap(MyType& a, MyType& b) noexcept { /* move-only logic */ },那std::is_swappable_v<mytype></mytype>会是true,且大概率高效;但 trait 本身不验证noexcept或是否用移动
常见误用:把 is_swappable 当成 swap 优化开关
有人试图这样写:
立即学习“C++免费学习笔记(深入)”;
if constexpr (std::is_swappable_v<T>) {
std::swap(a, b);
} else {
/* fallback */
}这毫无意义——std::swap 本身就有兜底实现(基于拷贝),而且只要 T 可拷贝,std::swap 就一定可用;std::is_swappable_v<t></t> 在这种上下文里既不增加安全性,也不提升性能。
真正该检查的是:
-
std::is_nothrow_swappable_v<t></t>:如果你需要强异常安全(比如容器resize中交换缓冲区) -
std::is_move_constructible_v<t> && std::is_move_assignable_v<t></t></t>:比单纯is_swappable更贴近“高效交换”的前提 - 或者直接依赖 ADL:写
using std::swap; swap(a, b);,让编译器自己找最优的swap,而不是绕一圈查 trait
std::is_swappable_v 的兼容性与陷阱
C++17 引入,但部分老编译器(如 GCC 7.5 之前)对类模板参数推导支持不全,可能导致 std::is_swappable_v<:vector>></:vector> 返回 false 即使实际可交换。
更隐蔽的问题是:它依赖 ADL 可见性。如果某类型只在私有命名空间提供 swap,且没用 using 引入,std::is_swappable_v 可能判为 false,尽管你在同作用域里能正常调用 swap。
所以别把它当运行时分支依据,也别用它替代实际测试——写个 static_assert 检查关键类型即可:
static_assert(std::is_swappable_v<MyHeavyClass>, "MyHeavyClass must be swappable");
真正难的从来不是“能不能 swap”,而是“swap 会不会悄悄拷贝、抛异常、或意外触发重载失败”。这些只能靠看定义、测行为、读文档。










