C++ 命名要求: 可交换(Swappable)
在此类型的任何左值或右值,都可以与某个其他类型的任何左值或右值进行交换,使用在 std::swap
和用户定义的 swap()
都可见的上下文中,进行不合格的函数调用 swap()
。
要求
如果对于类型 U 的任何对象 u
和类型 T 的任何对象 t
,类型 U 可以与类型 T 交换,则:
表达式 | 要求 | 语义 |
---|---|---|
| 调用后,t 的值是在调用前 u 持有的值,u 的值是在调用前 t 持有的值。 | 调用通过重载决议找到的名为 swap() 的函数,这些函数包括通过参数依赖查找找到的所有同名函数,以及在头文件 <algorithm> (直到 C++11)<utility> (自 C++11 起) 中定义的两个 std::swap 模板。 |
| 相同 | 相同 |
许多标准库函数(例如,许多算法)都要求其参数满足 Swappable,这意味着无论何时标准库执行交换,它都使用等同于 using std::swap; swap(t, u);
的方式。
典型的实现方式是:
-
在封闭命名空间中定义非成员
swap
,如果需要访问非公共数据成员,则可以转发给成员swap
。 -
在类中定义 友元函数(此方法将类特定的
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, char term = ' ') {
for(std::cout << rem << " { "; int e: v) { std::cout << e << ' '; }
std::cout << "}" << term;
}
};
void swap(IntVector& v1, IntVector& v2) {
v1.swap(v2);
}
int main()
{
IntVector v1{ {1,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 1 } v2 { 2222 2222 }
v1 { 2222 2222 } v2 { 1 1 1 1 1 }
v1 { 1 1 1 1 1 } v2 { 2222 2222 }
缺陷报告
以下改变行为的缺陷报告已追溯应用于先前发布的 C++ 标准。
DR | 应用于 | 发布时的行为 | 正确行为 |
---|---|---|---|
LWG 226 | C++98 | 标准库如何使用 swap 不明确。 | 明确为使用 std:: 和 ADL 查找的 swap 。 |