标准库如何实现std::swap



如何在STL中实现交换函数?是不是这么简单:

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

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

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,要求类型TMoveConstructibleMoveAssignable,从而允许实现和改进。

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

当您的实现比标准版本更有效或更具体时,通常建议为特定类型定制swap实现。

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

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

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

如何在STL中实现swap函数?

实现?它是一个规范,而不是一个具体的库。如果你的意思是我的编译器的标准库是怎么做的,要么告诉我们是哪个编译器,要么自己读代码。

是这么简单吗?

这实际上是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专门化可以访问vector的内部结构,并且可以交换两个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
}

请注意,这不涉及任何昂贵的复制,没有动态(de)分配,并且保证不会抛出。

现在,这种专门化的原因是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);
}

可以使用移动构造/赋值来获得与上述自定义向量实现基本相同的行为,而无需编写自定义实现。

最新更新