跳到主要内容

C++ 命名要求: 可交换(Swappable)

在此类型的任何左值或右值,都可以与某个其他类型的任何左值或右值进行交换,使用在 std::swap 和用户定义的 swap() 都可见的上下文中,进行不合格的函数调用 swap()

要求

如果对于类型 U 的任何对象 u 和类型 T 的任何对象 t,类型 U 可以与类型 T 交换,则:

表达式要求语义
#include <algorithm> // until C++11
#include <utility> // since C++11
using std::swap;
swap(u, t);
调用后,t 的值是在调用前 u 持有的值,u 的值是在调用前 t 持有的值。调用通过重载决议找到的名为 swap() 的函数,这些函数包括通过参数依赖查找找到的所有同名函数,以及在头文件 <algorithm> (直到 C++11)<utility> (自 C++11 起) 中定义的两个 std::swap 模板。
#include <algorithm> // until C++11
#include <utility> // since C++11
using std::swap;
swap(t, u);
相同相同

许多标准库函数(例如,许多算法)都要求其参数满足 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, 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 226C++98标准库如何使用 swap 不明确。明确为使用 std:: 和 ADL 查找的 swap

C++ 命名要求: 可交换(Swappable)

在此类型的任何左值或右值,都可以与某个其他类型的任何左值或右值进行交换,使用在 std::swap 和用户定义的 swap() 都可见的上下文中,进行不合格的函数调用 swap()

要求

如果对于类型 U 的任何对象 u 和类型 T 的任何对象 t,类型 U 可以与类型 T 交换,则:

表达式要求语义
#include <algorithm> // until C++11
#include <utility> // since C++11
using std::swap;
swap(u, t);
调用后,t 的值是在调用前 u 持有的值,u 的值是在调用前 t 持有的值。调用通过重载决议找到的名为 swap() 的函数,这些函数包括通过参数依赖查找找到的所有同名函数,以及在头文件 <algorithm> (直到 C++11)<utility> (自 C++11 起) 中定义的两个 std::swap 模板。
#include <algorithm> // until C++11
#include <utility> // since C++11
using std::swap;
swap(t, u);
相同相同

许多标准库函数(例如,许多算法)都要求其参数满足 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, 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 226C++98标准库如何使用 swap 不明确。明确为使用 std:: 和 ADL 查找的 swap