标准库如何实现标准: : 交换?

交换函数是如何在 STL 中实现的? 是否如此简单:

template<typename T> void swap(T& t1, T& t2) {
T tmp(t1);
t1=t2;
t2=tmp;
}

在其他文章中,他们讨论了如何为自己的类专门化这个函数。我为什么要这么做?为什么我不能使用 std::swap函数?

39865 次浏览

std::swap是如何实现的?

是的,问题中提到的实现是经典的 C + + 03。

更现代的(C + + 11) std::swap实现如下:

template<typename T> void swap(T& t1, T& t2) {
T temp = std::move(t1); // or T temp(std::move(t1));
t1 = std::move(t2);
t2 = std::move(temp);
}

这是对传统 C + + 03实现在资源管理方面的一个改进,因为它可以防止不需要的副本等等。它是 C + + 11 std::swap,要求类型 T可移动建筑物< em > MoveAssignable ,从而允许实现和改进。

为什么需要提供自定义实现?

对于特定类型的 swap自定义实现,通常建议在实现比标准版本更有效或更具体时使用。

这方面的一个经典(C + + 11之前)示例是,当您的类管理大量资源时,复制和删除这些资源的成本会很高。相反,您的自定义实现可以简单地交换影响交换所需的句柄或指针。

随着 std::move和可移动类型的出现(并实现了你的类型) ,大约 C + + 11等等,这里的许多原始基本原理开始消失; 但是,如果自定义交换比标准交换更好,那就实现它。

通用代码通常能够使用自定义 swap,如果它适当地使用了 ADL机制的话。

如何在 STL 中实现交换函数?

什么实现?它是一个规范,而不是一个单一的具体的库。如果你指的是 我的编译器的标准库是如何做到这一点的,要么告诉我们是哪个编译器,要么自己读代码。

就这么简单吗:

这基本上是 C + + 11之前的初级版本。

这种非专门化的实现强制执行一个副本: 对于您的示例中的 T = std::vector<SomethingExpensive>,代码翻译为:

template<typename T> void swap(T& t1, T& t2) {
T tmp(t1); // duplicate t1, making an expensive copy of each element
t1=t2;     // discard the original contents of t1,
// and replace them with an expensive duplicate of t2
t2=tmp;    // discard the original contents of t2,
// and replace them with an expensive duplicate of tmp
}            // implicitly destroy the expensive temporary copy of t1

为了交换两个向量,我们实际上创建了 。有三个动态分配和大量复制的昂贵对象,任何这些操作都可能抛出,可能使参数处于不确定状态。

由于这显然很糟糕,所以为昂贵的容器提供了重载,并且鼓励您为自己昂贵的类型编写重载: 例如,std::vector专门化可以访问向量的内部结构,并且可以交换两个向量而不需要进行所有的复制:

template <typename T> void swap(vector<T> &v1, vector<T> &v2) { v1.swap(v2); }
template <typename T> void vector<T>::swap(vector<T>& other) {
swap(this->size_, other.size_); // cheap integer swap of allocated count
swap(this->used_, other.used_); // cheap integer swap of used count
swap(this->data__, other.data_); // cheap pointer swap of data ptr
}

请注意,这不涉及任何昂贵的副本,不需要动态(反)分配,并且保证不会抛出。

现在,这种特殊化的原因是,Vector: : swap 可以访问 Vector 的内部结构,并且可以安全有效地移动它们而不需要复制。

为什么我需要做这个[为你自己的班级... ... 专门化] ?

Pre-C + + 11,与 std::vector的原因相同-使交换高效且异常安全。

自从 C + + 11以来,如果你提供 move 的构造和赋值,或者编译器可以为你生成合理的默认值,那么你真的不需要。

新的通用汽车互换:

template <typename T> void swap(T& t1, T& t2) {
T temp = std::move(t1);
t1 = std::move(t2);
t2 = std::move(temp);
}

可以使用 move 构造/赋值来获得与上面的自定义向量实现基本相同的行为,根本不需要编写自定义实现。