C++ 命名要求: Swappable (可交换)
来自 cppreference.cn
此类型的任何左值或右值都可以与某些其他类型的任何左值或右值交换,通过在 std::swap 和用户定义的 swap() 都可见的上下文中,进行非限定函数调用 swap()。
目录 |
[编辑] 要求
如果类型 U 的任何对象 u 和类型 T 的任何对象 t,满足以下条件,则类型 U 与类型 T 是可交换的:
表达式 | 要求 | 语义 |
---|---|---|
#include <algorithm> // 直到 C++11 #include <utility> // 自 C++11 起 |
调用后,t 的值是调用前 u 持有的值,u 的值是调用前 t 持有的值。 |
在通过实参依赖查找找到的所有同名函数和头文件<algorithm>(直到 C++11)<utility>(自 C++11 起)中定义的两个 std::swap 模板中,通过重载决议调用名为 swap() 的函数。 |
#include <algorithm> // 直到 C++11 #include <utility> // 自 C++11 起 |
相同 | 相同 |
许多标准库函数(例如,许多算法)都期望其参数满足 Swappable,这意味着每当标准库执行交换时,它都会使用等同于 using std::swap; swap(t, u); 的方式。
典型实现要么
1) 在包含命名空间中定义一个非成员 `swap`,如果需要访问非公有数据成员,则可以转发给成员 `swap`。
2) 在类内定义一个友元函数(这种方法会将特定于类的 `swap` 隐藏起来,除了 ADL 之外无法通过名称查找找到)。
[编辑] 注意
标准库函数执行交换时是否实际包含<algorithm>(直到 C++11)<utility>(自 C++11 起) 是未指定的,因此用户提供的 swap() 不应期望它被包含。
[编辑] 示例
运行此代码
#include <iostream> #include <vector> struct IntVector { std::vector<int> v; IntVector& operator=(IntVector) = delete; // not assignable void swap(IntVector& other) { v.swap(other.v); } void operator()(auto rem, auto term = " ") { std::cout << rem << "{{"; for (int n{}; int e : v) std::cout << (n++ ? ", " : "") << e; std::cout << "}}" << term; } }; void swap(IntVector& v1, IntVector& v2) { v1.swap(v2); } int main() { IntVector v1{{1, 1, 1, 1}}, v2{{2222, 2222}}; auto prn = [&]{ v1("v1", ", "), v2("v2", ";\n"); }; // std::swap(v1, v2); // Compiler error! std::swap requires MoveAssignable prn(); std::iter_swap(&v1, &v2); // OK: library calls unqualified swap() prn(); std::ranges::swap(v1, v2); // OK: library calls unqualified swap() prn(); }
输出
v1{{1, 1, 1, 1}}, v2{{2222, 2222}}; v1{{2222, 2222}}, v2{{1, 1, 1, 1}}; v1{{1, 1, 1, 1}}, v2{{2222, 2222}};
[编辑] 缺陷报告
下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。
缺陷报告 | 应用于 | 发布时的行为 | 正确的行为 |
---|---|---|---|
LWG 226 | C++98 | 不清楚标准库如何使用 swap |
澄清为同时使用 std:: 和通过 ADL 找到的 swap |
[编辑] 另见
(C++17)(C++17)(C++17)(C++17) |
检查一种类型的对象是否可以与相同或不同类型的对象进行交换 (类模板) |
(C++20) |
指定类型可以被交换,或者两种类型可以互相交换 (概念) |