复制赋值运算符是否应该将std :: swap作为一般规则?

时间:2014-09-19 20:44:38

标签: c++ c++11

总是使用std::swap来实现我的副本赋值运算符是一种很好的通用做法吗?我的理解是,这提供了一种共享复制构造函数实现的方法。我想避免重复实际的复制逻辑本身。所以这就是我要做的事情:

class Foo
{
public:
    Foo(Foo const& other) { /* assume valid implementation */ }

    Foo& operator= (Foo other)
    {
        std::swap(*this, other);
        return *this;
    }
};

将“other”传递给赋值运算符的行为执行复制构造(此时我们共享了复制逻辑)。我假设交换将调用移动构造(这里有一个编译器生成的实现)。

我一直在为我实现复制构造的每个类都这样做,因为分配&构造函数永远不会有不同的实现。

2 个答案:

答案 0 :(得分:10)

您的代码没有移动构造函数。您的复制构造函数阻止自动创建移动构造函数,并尝试移动您的类而不是复制它。

对于move-assign,您的operator=也会阻止其自动实现,并且可以在其位置使用。

最终结果是an infinite recursive call of = (live code)

如果遵循规则0,则既不需要复制构造函数,也不需要移动构造函数,移动赋值,复制赋值或析构函数。如果你写任何一个,你应该准备写所有这些。

使用std::swap可能很有用,但是因为你必须编写move-assign和move-construct,所以根据std::swap进行无限递归是等待发生的。

答案 1 :(得分:9)

如果Foo包含非静态数据成员std::vectorstd::string,或者包含包含vectorstring的数据成员(即使是间接的),那么这可能是一种非常有效的减慢代码速度的方法。它甚至可以比调用std::sleep_for更有效,因为后者不会在移动设备上浪费电池电量。

原因是调用vectorstring副本分配的副本分配有机会重用容器的容量。而swap成语始终会抛弃容量。

有关详细信息,请参阅我的ACCU 2014 talk, slides 43-53

请特别注意此性能图表,其中显示调用vector的复制赋值运算符与使用vector数据成员执行复制/交换习惯用语的速度增加。

this http://howardhinnant.github.io/accu_2014_48.pdf

充其量,复制/交换习惯用法与使用vector的复制分配一样快(当容量永远不够时)。在最坏的情况下(当容量总是足够时),复制/交换速度将快8倍。平均而言,复制/交换需要达到70%的速度(对于此测试)。