添加移动构造函数和移动赋值运算符什么时候才会真正开始产生影响?
考虑到当今编译器在返回值优化(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 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
即使您完全将优化方面排除在外,出于纯粹的语义原因,它已经有意义。想象一下一个类没有/不能实现复制的情况。例如
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!除了已经给出的优秀答案之外,我还想添加一些前瞻性的细节:
最新的 C++0x 草案中存在自动生成移动构造函数和移动赋值运算符的新规则。尽管这个想法并不是全新的,但最新的规则自 2010 年 10 月起才出现在草案中,尚未在编译器中广泛使用。
如果您的类没有用户声明的复制构造函数、复制赋值运算符、移动构造函数、移动赋值运算符和析构函数,编译器将为您提供默认的移动构造函数和移动赋值运算符。这些默认实现将简单地按成员方式移动所有内容。
您还可以显式默认移动成员:
请注意,如果这样做,您将隐式禁止复制语义,除非您显式提供或默认复制构造函数和复制赋值运算符。
在一个密切相关的最新更改中:如果您的类具有显式析构函数和隐式复制成员,那么这些成员的隐式生成现在已被弃用(委员会希望在存在显式析构函数时删除隐式复制生成,但由于向后兼容性而不能)。
总之,只要声明了析构函数,您就应该考虑显式声明复制和移动成员。
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:
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.
根据经验,只要您有保存(有条件地)动态分配内存的成员变量,我就会说添加一个移动构造函数。在这种情况下,如果您可以仅使用现有内存并为移动源提供所需的最小分配,以便它仍然可以运行(意味着被销毁),那么通常会更便宜。成员变量的数量并不重要,因为对于不涉及动态内存的类型,无论复制还是移动它们都不太可能产生影响(即使移动构造函数也必须以某种方式将它们从一个内存位置复制到另一个内存位置)。
因此,当
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
通常,当类拥有某种资源时,移动是有意义的,其中副本将涉及复制该资源,而移动则不会。最简单的例子是动态分配的内存。然而,值得注意的是,编译器会自动生成移动构造函数和运算符,就像复制一样。
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.
无论编译器可能自动完成什么,我都会说:
std::vector
成员)换句话说,如果移动可以比复制更有效地完成某些事情,那么添加它就有意义了。在您的
really_trivial
中,移动的效率只能与副本的效率相同。Irrespective of anything that might be automatically done by the compiler I'd say that:
std::vector
members)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.