C++移动分配可防止复制交换习惯用法
C++ move-assignment prevents copy-swap idiom
在C++中,复制交换习语通常实现如下:
C& operator=(C rhs)
{
swap(*this, rhs);
return *this;
}
现在,如果我想添加一个移动分配运算符,它应该看起来像这样:
C& operator=(C&& rhs)
{
swap(*this, rhs);
return *this;
}
但是,这会造成应该调用哪个赋值运算符的歧义,编译器理所当然地抱怨它。所以我的问题如下:如果我想支持复制交换习语和移动赋值语义,我应该怎么做?
或者这不是一个问题,因为有一个移动-复制构造函数和复制-交换习惯用法,一个人并没有真正受益于一个移动赋值运算符?
在提出这个问题之后,我编写了一段代码,演示了移动赋值可能导致比复制交换习惯用法更少的函数调用。首先让我介绍一下我的复制交换版本。请耐心等待;它看起来像一个很长但很简单的例子:
#include <algorithm>
#include <iostream>
#include <new>
using namespace std;
bool printOutput = false;
void* operator new(std::size_t sz)
{
if (printOutput)
{
cout << "sz = " << sz << endl;
}
return std::malloc(sz);
}
class C
{
int* data;
public:
C() : data(nullptr)
{
if (printOutput)
{
cout << "C() called" << endl;
}
}
C(int data) : data(new int)
{
if (printOutput)
{
cout << "C(data) called" << endl;
}
*(this->data) = data;
}
C(const C& rhs) : data(new int)
{
if (printOutput)
{
cout << "C(&rhs) called" << endl;
}
*data = *(rhs.data);
}
C(C&& rhs) : C()
{
if (printOutput)
{
cout << "C(&&rhs) called" << endl;
}
swap(*this, rhs);
}
C& operator=(C rhs)
{
if (printOutput)
{
cout << "operator= called" << endl;
}
swap(*this, rhs);
return *this;
}
C operator+(const C& rhs)
{
C result(*data + *(rhs.data));
return result;
}
friend void swap(C& lhs, C& rhs);
~C()
{
delete data;
}
};
void swap(C& lhs, C& rhs)
{
std::swap(lhs.data, rhs.data);
}
int main()
{
C c1(7);
C c2;
printOutput = true;
c2 = c1 + c1;
return 0;
}
我已经使用 -fno-elide-constructors 选项使用 g++ 编译了它,因为我想看到没有优化行为。结果如下:
sz = 4
C(data) called // (due to the declaration of result)
C() called // (called from the rvalue copy-constructor)
C(&&rhs) called // (called due to copy to return temporary)
C() called // (called from the rvalue copy-constructor)
C(&&rhs) called // (called due to pass-by-value in the assignment operator)
operator= called
现在,如果我选择不在赋值运算符中制作复制交换习惯用法,我将得到如下所示的内容:
C& operator=(const C& rhs)
{
if (printOutput)
{
cout << "operator=(const C&) called" << endl;
}
if (this != &rhs)
{
delete data;
data = new int;
*data = *(rhs.data);
}
return *this;
}
这允许我拥有如下移动分配运算符:
C& operator=(C&& rhs)
{
if (printOutput)
{
cout << "operator=(C&&) called" << endl;
}
swap(*this, rhs);
return *this;
}
现在,在其他一切都相同的情况下,我得到以下输出:
sz = 4
C(data) called // (due to the declaration of result)
C() called // (called from the rvalue copy-constructor)
C(&&rhs) called // (called due to copy to return temporary)
operator=(C&&) called // (move-assignment)
如您所见,这会导致更少的函数调用。实际上,copySwapIdiom 中的最后三个函数调用现在已经下降到单个函数调用。这是意料之中的,因为我们不再按值传递赋值运算符参数,因此那里不会发生构造。
但是,我并没有从赋值运算符中复制交换习语的美感中受益。任何见解都非常感谢。
如果提供有效的移动构造函数,则实际上不需要实现移动赋值运算符。
class Foo
{
public:
explicit Foo(Bar bar)
: bar(bar)
{ }
Foo(const Foo& other)
: bar(other.bar)
{ }
Foo(Foo&& other)
: bar(other.bar)
{ }
// other will be initialized using the move constructor if the actual
// argument in the assignment statement is an rvalue
Foo& operator=(Foo other)
{
std::swap(bar, other.bar);
return *this;
}
此处复制交换习惯用法背后的动机是将复制/移动工作转发给构造函数,这样您就不会重复构造函数和赋值运算符的工作。可是
C& operator=(C rhs) noexcept;
替换对的方法
C& operator=(const C& rhs);
C& operator=(C&& rhs) noexcept;
C& operator=(C rhs) noexcept;
执行复制分配还是移动分配取决于rhs
的构造方式。例如
a = std::move(b); // rhs is move-constructed from r-value std::move(b), and thus move-assignment
c = d; // rhs is copy-constructed from l-value d, and thus copy-assignment
相关文章:
- C++嵌套if语句,基本货币交换
- shell排序中的交换和比较
- 排序时无法执行交换操作.我做的时候它会崩溃.为什么
- 通过交换元素使数组相同
- 复制和交换习惯用法与移动操作之间的交互
- 复制交换习惯用法-我们可以在这里使用动态强制转换操作吗
- 错误:使用复制和交换习惯用法的交换函数中"operator="的重载不明确
- C++移动分配可防止复制交换习惯用法
- 副本交换习惯用法的有效性
- 当涉及分配器时,是否有类似于复制和交换习惯用法的东西
- 是具有复制和交换习惯用法的复制赋值运算符,建议进行自赋值检查
- 使用复制和交换习惯用法,复制对象的析构函数如何不解除分配指向内存
- 在复制和交换习惯用法中实现交换
- 为什么内存泄漏只发生在赋值运算符重载的情况下,而不是在复制构造函数中,以及复制和交换习惯用法如何解决它
- C++:复制和交换习惯用法,替代构造函数
- 如何最好地处理具有未初始化内存的复制交换习惯用法
- 如何实现拷贝交换习惯用法的复制构造函数
- 移动构造函数和赋值操作符,使用复制-交换习惯实现
- 重用复制-交换习惯用法
- 为什么SGI STL不使用复制和交换习惯用语?