是专门的 std::swap 现在我们已经有了移动语义

Is specializing std::swap deprecated now that we have move semantics?

本文关键字:语义 移动 我们 std swap      更新时间:2023-10-16

可能的重复项:
移动语义 == 自定义交换函数过时了?

这是std::swap在 C++11 中的样子:

template<typename T>
void swap(T& x, T& y)
{
  T z = std::move(x);
    x = std::move(y);
    y = std::move(z);
}

我是否仍然需要为我自己的类型专门化std::swap,或者std::swap会尽可能高效,当然,前提是我的类定义了移动构造函数和移动赋值运算符?

std::swap的专用化现在是可选的,但未弃用。 基本原理是性能。

对于原型代码,甚至可能对于许多交付代码,std::swap将非常快。 但是,如果您处于需要从代码中获取每一点的情况,那么编写自定义交换仍然是一个显着的性能优势。

考虑以下情况:您的类基本上有一个拥有指针,而您的移动构造函数和移动赋值只需要处理该指针。 计算每个成员的机器负载和存储:

移动构造函数:1 个加载和 2 个存储。

移动分配:2 个装载和 2 个商店。

自定义交换:2 个加载和 2 个存储。

std::swap是 1 个移动构造和 2 个移动分配,或:5 个负载和 6 个存储。

自定义交换可能仍然比std::swap快两到三倍。 尽管任何时候您都试图通过计算负载和存储来计算某物的速度,但两者都会很快变得邪恶。

注意:在计算移动分配的成本时,请确保并考虑到您将移动到移出值(在std::swap算法中(。 这通常会抵消解除分配的成本,尽管是以分支为代价的。

既然我们有移动语义,是否std::swap弃用专用?

不。这是通用版本,但您可以对其进行优化以跳过第三次移动操作。我的偏好是将复制和交换习语与为我的类自定义std::swap相结合。

这意味着我将拥有:

class Aaaa
{
public:
    Aaaa(); // not interesting; defined elsewhere
    Aaaa(Aaaa&& rvalueRef); // same
    Aaaa(const Aaaa& ref); // same
    ~Aaaa(); // same
    Aaaa& operator=(Aaaa object) // copy&swap
    {
        swap(object);
        return *this;
    }
    void swap(Aaaa& other)
    {
        std::swap(dataMember1, other.dataMember1);
        std::swap(dataMember2, other.dataMember2);
        // ...
    }
    // ...
};
namespace std
{
    template<> inline void std::swap(Aaaa& left, Aaaa& right)
    { left.swap(right); }
}

这将取决于您的类型。

您将它从 x 移动到 z,从 y 移动到

x,从 z 移动到 y。这是底层表示的三个复制操作(也许只有一个指针,也许更多,谁知道呢(

现在,也许您可以为您的类型创建一个更快的交换(异或交换技巧,内联汇编器,或者您的底层类型的 std::swap 只是更快(。

或者,也许您的编译器也擅长优化,并且基本上将这两种情况优化为相同的指令(例如在寄存器中具有临时指令(。

我个人倾向于总是实现一个交换成员函数,该函数将从多个位置调用,包括移动分配之类的东西,但是 YMMV。

swap()调用一个移动构造函数和 2 个移动赋值。我认为一个人可以为他特定类型的类编写更有效的swap(),例如,

class X
{
    int * ptr_to_huge_array;
public:
// ctors, assgn ops. etc. etc.
    friend void swap(X& a, X& b)
    {
        using std::swap;
        swap(a.ptr_to_huge_array, b.ptr_to_huge_array);
    }
};

无论移动构造函数和赋值运算符的实现如何。