我应该在什么条件下考虑实现移动构造函数和移动运算符

Under what conditions should I be thinking about implementing a move constructor and a move operator?

本文关键字:移动 构造函数 运算符 实现 在什么 条件下 我应该      更新时间:2023-10-16

对于标准的复制构造函数和赋值运算符,如果我的类实现了析构函数,我总是考虑实现它们或delete

默认值。

对于新的移动构造函数移动运算符,考虑是否需要实现的正确方法是什么?

作为从 pre-C++0x 转换系统的第一步,我可以只delete默认的移动构造函数移动运算符,还是应该不理会它们?

不必担心它,从某种意义上说,当您用户声明析构函数(或 12.8/9 中列出的任何其他内容(时,这会阻止生成默认的移动构造函数。因此,与副本的风险不同,即默认是错误的。

所以,作为第一关,不要管他们。在现有代码中,C++11 移动语义可能允许移动,而 C++03 指示副本。你的类将继续被复制,如果这在 C++03 中没有导致性能问题,那么我无法立即想到为什么它会在 C++11 中出现任何原因。如果它确实在 C++03 中导致性能问题,那么您有机会修复代码中以前从未解决过的错误,但这是一个机会,而不是义务;-(

如果您稍后实现移动构造和赋值,它们将被移动,特别是如果您认为类的 C++11 个客户端不太可能使用"交换化"来避免复制,更有可能按值传递您的类型等,则您需要这样做C++03 客户端。

在 C++11 中编写新类时,您需要在与 C++03 中考虑和实现swap的相同标准下考虑和实现 move。可以复制的类实现了 C++11 的"可移动"概念(就像可以在 C++03 中复制的类可以通过 std 中的默认实现进行交换一样(,因为"可移动"没有说明源处于什么状态 - 特别是它允许保持不变。因此,副本作为移动是有效的,它不一定是有效的,对于许多类,您会发现与"好"的移动或交换不同,副本可以抛出。

您可能会发现,在具有析构函数(因此没有默认移动构造函数(的情况下,

必须为类实现 move,并且您还有一个可移动但不可复制的数据成员(因此也没有默认的复制构造函数(。这时,移动在语义和性能上都变得很重要。

对于 C++11,由于库的编写方式,您很少需要提供析构函数或复制语义。编译器提供的成员几乎总是做得很好(前提是它们正确实现:MSVC 迫使您手动实现许多移动语义,这非常麻烦(。

如果必须实现自定义析构函数,请使用以下方法:

  • 实现一个移动构造函数和一个按值获取的赋值运算符(使用 copy&swap:请注意,你不能使用 std::swap,因为它使用赋值。您必须自己提供私人交换(。注意异常保证(查找std::move_if_noexcept(。
  • 如有必要,实现复制构造函数。否则,请将其删除。请注意,非默认复制语义很少有意义。
此外,虚拟析构函数

算作自定义析构函数:在声明虚拟析构函数时提供或删除复制 + 移动语义。

由于它们被用作优化,如果优化适用于您的类,则应实现它们。如果您可以从即将销毁的临时对象中"窃取"类持有的内部资源。std::vector 是一个完美的例子,其中 move 构造函数只将指针分配给内部缓冲区,将临时对象留空(有效地窃取元素(。