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 meaningfu
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!
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
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.
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.
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.