添加移动构造函数和移动赋值运算符什么时候才会真正开始产生影响?

发布于 2024-10-12 10:43:41 字数 621 浏览 11 评论 0原文

考虑到当今编译器在返回值优化(RVO 和 NRVO)方面的高质量,我想知道开始添加移动构造函数和移动赋值运算符实际上有意义的类复杂性是多少。

例如,对于这个really_trivial类,我只是假设移动语义不能提供比RVO和NRVO在复制它的实例时已经提供的更多的东西:

class really_trivial
{
    int first_;
    int second_;

public:

    really_trivial();
    ...
};

在这个semi_complex类中,我会毫不犹豫地添加移动构造函数和移动赋值运算符:

class semi_complex
{
    std::vector<std::string> strings_;

public:

    semi_complex(semi_complex&& other);
    semi_complex& operator=(semi_complex&& other);
    ...
};

那么,添加移动构造函数和移动赋值运算符的成员变量的数量和类型才有意义呢?

Considering the high quality of today's compilers regarding return value optimization (both RVO and NRVO), I was wondering at what class complexity it's actually meaningful to start adding move constructors and move assignment operators.

For instance, for this really_trivial class, I just assume that move semantics cannot offer anything more than RVO and NRVO already does when copying around instances of it:

class really_trivial
{
    int first_;
    int second_;

public:

    really_trivial();
    ...
};

While in this semi_complex class, I'd add a move constructor and move assignment operator without hesitation:

class semi_complex
{
    std::vector<std::string> strings_;

public:

    semi_complex(semi_complex&& other);
    semi_complex& operator=(semi_complex&& other);
    ...
};

So, at what amount and of what kinds of member variables does it start making sense to add move constructors and move assignment operators?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

甜味拾荒者 2024-10-19 10:43:41

即使您完全将优化方面排除在外,出于纯粹的语义原因,它已经有意义。想象一下一个类没有/不能实现复制的情况。例如 boost::scoped_ptr 无法复制,但可以移动!

It is already meaningful for purely semantic reasons, even if you keep the optimization-aspects out completely. Just imagine the case where a class does not/cannot implement copying. For example boost::scoped_ptr cannot be copied, but it can be moved!

凉城凉梦凉人心 2024-10-19 10:43:41

除了已经给出的优秀答案之外,我还想添加一些前瞻性的细节:

最新的 C++0x 草案中存在自动生成移动构造函数和移动赋值运算符的新规则。尽管这个想法并不是全新的,但最新的规则自 2010 年 10 月起才出现在草案中,尚未在编译器中广泛使用。

如果您的类没有用户声明的复制构造函数、复制赋值运算符、移动构造函数、移动赋值运算符和析构函数,编译器将为您提供默认的移动构造函数和移动赋值运算符。这些默认实现将简单地按成员方式移动所有内容。

您还可以显式默认移动成员:

semi_complex(semi_complex&&) = default;
semi_complex& operator=(semi_complex&&) = default;

请注意,如果这样做,您将隐式禁止复制语义,除非您显式提供或默认复制构造函数和复制赋值运算符。

在一个密切相关的最新更改中:如果您的类具有显式析构函数和隐式复制成员,那么这些成员的隐式生成现在已被弃用(委员会希望在存在显式析构函数时删除隐式复制生成,但由于向后兼容性而不能)。

总之,只要声明了析构函数,您就应该考虑显式声明复制和移动成员。

In addition to the excellent answers already given, I would like to add a few forward-looking details:

There are new rules in the latest C++0x draft for automatically generating a move constructor and move assignment operator. Although this idea is not entirely new, the latest rules have only been in the draft since Oct. 2010, and not yet widely available in compilers.

If your class does not have a user-declared copy constructor, copy assignment operator, move constructor, move assignment operator and destructor, the compiler will provide defaulted move constructor and move assignment operator for you. These default implementations will simply member-wise move everything.

You can also explicitly default your move members:

semi_complex(semi_complex&&) = default;
semi_complex& operator=(semi_complex&&) = default;

Note that if you do so, you will implicitly inhibit copy semantics unless you explicitly supply or default the copy constructor and copy assignment operator.

In a closely related late-breaking change: If your class has an explicit destructor, and implicit copy members, the implicit generation of those members is now deprecated (the committee would like to remove the implicit generation of copy when there is an explicit destructor, but can't because of backwards compatibility).

So in summary, any time you have a destructor declared, you should probably be thinking about explicitly declaring both copy and move members.

梓梦 2024-10-19 10:43:41

根据经验,只要您有保存(有条件地)动态分配内存的成员变量,我就会说添加一个移动构造函数。在这种情况下,如果您可以仅使用现有内存并为移动源提供所需的最小分配,以便它仍然可以运行(意味着被销毁),那么通常会更便宜。成员变量的数量并不重要,因为对于不涉及动态内存的类型,无论复制还是移动它们都不太可能产生影响(即使移动构造函数也必须以某种方式将它们从一个内存位置复制到另一个内存位置)。

因此,当

  • 涉及动态内存分配
  • 时,移动语义对于一个或多个成员变量来说是有意义的(这意味着那些涉及沿线某处的动态分配)。

As a rule of thumb I would say add a move constructor whenever you have member variables which hold (conditionally) dynamically allocated memory. In that case its often cheaper if you can just use the existing memory and give the move source the minimal allocation it needs so it can still function (meaning be destroyed). The amount of member variables doesn't matter that much, because for types not involving dynamic memory its unlikely to make a difference whether you copy or move them (even the move constructor will have to copy them from one memory location to another somehow).

Therefore move semantices make sense, when

  • dynamic memory allocation is involved
  • move makes sense for one or more member variables (which means those involve dynamic allocation somewhere along the line).
摇划花蜜的午后 2024-10-19 10:43:41

通常,当类拥有某种资源时,移动是有意义的,其中副本将涉及复制该资源,而移动则不会。最简单的例子是动态分配的内存。然而,值得注意的是,编译器会自动生成移动构造函数和运算符,就像复制一样。

Typically, moving makes sense when the class holds some kind of resource, where a copy would involve copying that resource and moving would not. The easiest example is dynamically allocated memory. However, it is worth noting that the compiler will automatically generate move constructors and operators, just like it will for copying.

月光色 2024-10-19 10:43:41

无论编译器可能自动完成什么,我都会说:

  • 如果任何成员具有有意义且有益的移动语义,则该类也应该具有此移动语义。 (-> std::vector 成员)
  • 如果复制时涉及任何动态分配,则移动操作有意义。

换句话说,如果移动可以比复制更有效地完成某些事情,那么添加它就有意义了。在您的really_trivial中,移动的效率只能与副本的效率相同。

Irrespective of anything that might be automatically done by the compiler I'd say that:

  • If any member has meaningful and beneficial move semantics, that the class should have this move semantics too. (-> std::vector members)
  • If any dynamic allocations are involved when copying, move operations make sense.

Put otherwise, if move can do something more efficiently than copy, it makes sense to add it. In your really_trivial a move could only be as efficient as the copy already is.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文