c++ - 使用单个函数实现复制和 move 赋值

标签 c++ c++11 move assignment-operator library-design

通常,给定一些类型T,要实现复制和 move 赋值,需要两个函数

T& operator=(T&&) { ... }
T& operator=(const T&) { ... }

最近才发现,一个就够了

T& operator=(T v) {
  swap(v);
  return *this;
}

这个版本利用了复制/move 构造函数。赋值是复制还是 move 取决于 v 的构造方式。这个版本甚至可能比第一个版本更快,因为按值传递为编译器优化提供了更多空间 [1]。那么,第一个版本比第二个版本有什么优势,即使是标准库也使用它?

[1] 我猜这解释了为什么标签和函数对象在标准库中按值传递。

最佳答案

std::swap 是通过执行 move 构造后跟两个 move 赋值操作来实现的。因此,除非您实现自己的 swap 操作来替换标准提供的操作,否则您提供的代码是一个无限循环。

所以你要么实现2个operator=方法,要么实现一个operator=方法和一个swap方法。就调用的函数数量而言,它最终是相同的。

此外,您的 operator= 版本有时效率较低。除非省略参数的构造,否则该构造将通过从调用者的值复制/move 来完成。接下来是 1 个 move 构造和 2 个 move 分配(或者您的 swap 所做的任何事情)。而适当的 operator= 重载可以直接使用给定的引用。

这假设您无法编写实际作业的最佳版本。考虑将一个 vector 复制分配给另一个。如果目标 vector 有足够的存储空间来容纳源 vector 的大小......你不需要分配。而如果您复制 construct,则必须分配存储空间。只有这样才能释放您本可以使用的存储空间。

即使在最好的情况下,您的复制/move 和交换也不会比使用值有效。毕竟,您将引用参数; std::swap 不适用于值。因此,无论您认为使用引用会失去什么效率,都会以任何一种方式失去。

支持复制/move 和交换的主要论点是:

  1. 减少代码重复。这只有在您的复制/move 分配操作的实现与复制/move 构造或多或少相同时才有用。许多类型并非如此。如前所述,vector 可以通过尽可能使用现有存储来优化自身。事实上,许多容器都可以(特别是排序容器)。

  2. 以最小的努力提供强大的异常保证。假设您的 move 构造函数是 noexcept。

就个人而言,我更愿意完全避免这种情况。我更喜欢让编译器生成我所有的特殊成员函数。而如果一个类型绝对需要我写那些特殊的成员函数,那么这个类型将尽可能地精简。也就是说,它的唯一目的是管理需要此操作的任何内容。

那样的话,我就不用担心了。我的大部分类都不需要显式定义这些函数中的任何一个。

关于c++ - 使用单个函数实现复制和 move 赋值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34804343/

相关文章:

java - 将文件从 SD 卡 move 到应用程序内部数据(存储)

curl - 使用 cURL 将 jenkins 作业从一个 View move 到另一个 View 的命令

c++ - 如何将状态栏放置在OPENGL之上?

c++ - 传递带有模板化参数的函数作为参数,带有函数指针与 std::function

c++ - 为什么我的重载 << 不返回任何数据?

c++ - 另一个线程完成后立即生成一个新线程

c# - 如何在 WPF 窗口中 move 网格面板

c++ - C++ 宏中的模板?

c++ - 如何为元组参数专门化一个函数?

c++ - 指向成员对象的成员指针和声明顺序