始终使用std::swap
实现我的复制赋值运算符是一种好的常规做法吗?我的理解是,这提供了一种共享复制构造函数实现的方法。我希望避免复制实际的复制逻辑本身。所以我要做的是:
class Foo
{
public:
Foo(Foo const& other) { /* assume valid implementation */ }
Foo& operator= (Foo other)
{
std::swap(*this, other);
return *this;
}
};
将"other"传递到赋值运算符中的行为执行复制构造(在这一点上,我们已经共享了复制逻辑)。我假设交换将调用move构造(这里有一个编译器生成的实现)。
自从作业&构造函数从来没有不同的实现。
您的代码没有move构造函数。你的复制构造函数阻止了移动构造函数的自动创建,而试图移动你的类却复制了它
对于移动分配,您的operator=
也会阻止其自动实现,并且可以在其位置使用。
最终结果是=
(实时代码)的无限递归调用。
如果遵循0规则,则不需要复制构造函数、移动构造函数、移动赋值、复制赋值或析构函数。如果你写了其中的任何一个,你应该准备好写所有的。
使用std::swap
可能很有用,但因为您必须编写移动分配和移动构造,所以根据std::swap
执行任何一项都是等待发生的无限递归。
如果Foo
包含非静态数据成员std::vector
或std::string
,或者包含包含vector
或string
的数据成员(即,甚至是间接的),那么这可能是降低代码速度的一种非常有效的方法。它甚至可以比调用std::sleep_for
更有效,因为后者不会在移动设备上浪费电池电量。
原因是调用vector
或string
副本分配的副本分配有机会重用容器的容量。而swap
习语总是丢弃容量。
有关更多信息,请参阅我的ACCU 2014演讲,幻灯片43-53。
特别要注意的是,此性能图显示了调用vector
的复制分配运算符与使用vector
数据成员执行复制/交换习惯用法相比的速度提高。
这个http://howardhinnant.github.io/accu_2014_48.pdf
拷贝/交换习惯用法的速度最好与使用vector
的拷贝分配一样快(当容量永远不够时)。在最坏的情况下(当容量总是足够时),拷贝/交换速度会慢近8倍。平均而言,复制/交换需要70%的速度命中(对于此测试)。