38

可能重复:
移动语义 == 自定义交换功能已过时?

这是std::swapC++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尽可能高效?

4

4 回答 4

36

的专业化std::swap现在是可选的,但不被弃用。理由是性能。

对于原型代码,甚至可能是大量交付代码,std::swap将非常快。但是,如果您需要从代码中找出每一点,编写自定义交换仍然可以带来显着的性能优势。

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

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

移动分配:2 次装载和 2 次存储。

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

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

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

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

于 2011-12-23T15:04:55.067 回答
4

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); }
}
于 2011-12-23T15:01:05.653 回答
0

这将取决于您的类型。

你会将它从 x 移动到 z,从 y 移动到 x,从 z 移动到 y。那是底层表示的三个复制操作(也许只有一个指针,也许还有更多,谁知道)

现在,也许您可​​以为您的类型创建更快的交换(xor 交换技巧、内联汇编程序,或者您的基础类型的 std::swap 可能更快)。

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

我个人倾向于始终实现一个交换成员函数,该函数将从多个地方调用,包括移动赋值之类的东西,但 YMMV。

于 2011-12-23T14:48:37.243 回答
0

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);
    }
};

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

于 2011-12-23T14:49:34.300 回答