Move Semantics are a game changer on how we return values and pass arguments in C++11. In this post we concern ourselves with the best way to implement an assignment (‘=‘) operator. If rvalues and move semantics are all new for you, you might want to read Michael’s post I like to std::move it first.
For many C++ developers the canonic implementation of the assignment operator is something close to this:
my_value_type& operator=(my_value_type const& other) { my_value_type copy(other); this->swap(copy); return *this; }
This is already pretty nice, since we do not have to worry about self assignment or forgetting any members. Provided swap does not throw we can even give a strong exception safety guarantee. An improvement, however, is still possible, consider this snippet:
my_value_type& operator=(my_value_type other) { this->swap(other); return *this; }
The copy is now made implicitly in the signature, rather than explicitly in the body. People who have written a lot of C++ code in the past may almost feel physical pain when they see an argument passed by value. That is mainly for two good reasons:
- Decoupling:
For parameters passed by reference, a forward declaration in the header is sufficient. - Efficiency:
For most types a copy is more expensive than passing a reference.
Well, decoupling shouldn’t be too much of an issue for our assignment operator since the only argument it takes is an instance of the type it is declared for. We do not mind if my_value_type depends on itself. So what about efficiency? Well, this turns out to be a strong point of the second implementation. Consider this line of code:
my_value_type function(); //definiton not important for this example my_value_type instance; //somewhere else in the code... instance = function();
This line invokes our assignment operator. If the assignment operator takes a constant reference, the temporary (or rvalue) returned by function is copied and then thrown away. Taking a copy and throwing the original away is not very efficient. Taking the parameter by value does a curious thing. The fact that we take a copy is no longer an implementation detail of the operator’s definition, but made publicly visible to the compiler in the signature. The compiler will invoke my_value_type‘s move constructor to create the parameter from the temporary, allowing for resource stealing optimizations.
What if my_value_type does not posess a move constructor? We are still only making one copy(In the scope of this post we are not taking into account return value optimization).
So to summerize it up:
We get better performance and all we have to do for this is write one line less.
See this article from 4 years ago on the same subject, by Dave Abrahams: http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/
Regards,
&rzej
Thanks Andrzej. Dave’s Blog is truly remarkable.
Moving a string is almost free; it merely assigns the values of the source s data members to the corresponding data members of the target. In contrast, copying a string requires the allocation of dynamic memory and copying the characters from the source.