"std::swap"应用于这些对象时会做什么?

What does `std::swap` do when applied to these objects?

本文关键字:quot 什么 std swap 应用于 对象      更新时间:2023-10-16

代码

using namespace std;
class A 
{
private:
vector<int> a;
public:
A(vector<int> x):a(x){}
string toString()
{
string s;
for (auto& element : a)
{
s += to_string(element) + " ";
}
return s;
}
};
int main()
{
A a1({1,2,3});
A a2({11,12,13});
cout << "a1 = " << a1.toString() << "n";
cout << "a2 = " << a2.toString() << "n";

swap(a1,a2);

cout << "a1 = " << a1.toString() << "n";
cout << "a2 = " << a2.toString() << "n";

return 0;
}

按预期输出

a1 = 1 2 3                                                                                                            
a2 = 11 12 13                                                                                                         
a1 = 11 12 13                                                                                                         
a2 = 1 2 3 

从 cplusplus.com> std::swap 在复杂性

非数组:常量:只执行一个构造和两个赋值(尽管请注意,这些操作中的每一个都有自己的复杂性)。

数组:线性 N 中的:对每个元素执行交换操作。

这是否意味着std::swap应用于a1a2时仅交换指向数组的指针[1,2,3][11,12,13],但不复制任何int或其他任何东西?

当应用于类A的两个对象时,std::swap到底做了什么?

假设std::swap复制数组的所有元素,我是否应该编写一个static A::swap函数,使用时间复杂度恒定的vector::swap(来自 cplusplus.com> vector::swap),这意味着它只交换指针?


[..] 想要添加一条注释,说明std::swap的语义在 C++17 中发生了更改。因此,提及编译器,版本以及目标标准可能是个好主意。

我希望一个简单的问题不会给C++标准和编译器版本带来复杂性。我通常在 C++11 中编译我的代码。为了完整起见,这是我笔记本电脑上的 gcc 版本。

$ gcc --version
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.13.sdk/usr/include/c++/4.2.1
Apple LLVM version 9.0.0 (clang-900.0.39.2)
Target: x86_64-apple-darwin17.3.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin

要求 type 参数std::swap模板 是 MoveConstructible 和 MoveAssignable。这表明swap可以大致写成(省略了几位)

void swap(T &a, T &b) {
T tmp{std::move(a)};
a = std::move(b);
b = std::move(tmp);
}

对于您的示例类,它将调用默认的移动 ctor/move 赋值运算符(编辑:A)几次,它们又会调用std::vector的运算符。 IOW,您可以期望您的程序按原样合理高效。

或者,可以在与A相同的命名空间中定义非成员swap函数,并使用 vector 参数显式调用std::swap。或直接拨打std::vector::swap

根据您的编译器和您使用的类/类型,交换函数将复制其中一个类,或使用移动构造函数/赋值(C++11 及更高)(http://en.cppreference.com/w/cpp/utility/move)。

您的类仅包含一个向量,对于向量类,此移动构造函数执行您所说的简单"交换指针",因此它将非常快。请注意,实现例如复制构造函数将删除隐式声明的移动构造函数,这使得这种"魔术"发生!!(http://en.cppreference.com/w/cpp/language/move_constructor#Implicitly-declared_move_constructor)

在独立查看交换函数时,我为您提供了大量后续见解: 交换函数最著名的用法之一发生在复制和交换习语(https://progdoo.wordpress.com/2012/06/03/c11-copy-and-swap-idiom/)中。这最有效地适用于已实现的移动构造函数/赋值。