Consider this simple C ++ code:
class StringHolder { std::string member; public: StringHolder(const std::string &newMember) : member(newMember) {} }; std::string value = "I am a string that will probably be heap-allocated."; StringHolder hold(value);
After completing the second line, how many copies of the line exist? The answer is two: one is stored in value , and one is stored in hold . This is normal ... sometimes. Often there are times when you want to give someone a copy of a string, keeping it for yourself. But there are times when you also do not want this. For example:
StringHolder hold("I am a string that will probably be heap-allocated.");
This will create a temporary std::string element, which will then be passed to the StringHolder constructor. The constructor will copy-build your member. After the constructor completes, temporary information will be destroyed. At some point, we had two copies of the string, for no reason.
There was no point in having two copies of the string. We would like to move the std::string parameter to StringHolder , so that there will be only one copy of the string.
What happens when the structure moves.
A std::string is just a wrapper around a pointer to a selected array of characters and a size containing the length of this array (and capacity, but no matter what it is now). If you have std::string , and you want to move it to another, then the new line should require ownership of the allocated array of characters, and the old line should give up ownership. In C ++ 03, you can do this using the swap operation:
std::string oldStr = "I am a string that will probably be heap-allocated."; std::string newStr; std::swap(newStr, oldStr);
This moves the contents of oldStr to newStr without allocating memory.
The C ++ 11 relocation syntax provides two important functions that std::swap does not support.
First, moving can be done implicitly (but only when it's safe). You must explicitly call swap if you want to swap; movement can occur by writing natural code. For example, take our StringHolder from earlier and make one change:
class StringHolder { std::string member; public: StringHolder(std::string newMember) : member(std::move(newMember)) {} }; StringHolder hold("I am a string that will probably be heap-allocated.");
How many copies of this line have ever been created? The answer ... only one: the construction of a temporary. Since it is temporary, C ++ 11 is smart enough to know that it can move-build anything that it initializes. Therefore, it moves - it creates a value parameter of the StringHolder constructor (or, most likely, completely excludes the construction). This moves the stored memory from temporary to newMember . Therefore, copying does not occur.
After that, we explicitly call the move constructor when building member . This again moves the allocated memory from newMember to member .
We select a line only once. This can be a big performance savings.
Now, how does this apply to the constructors of your own types? Well, consider this code:
class StringHolder { std::string member; public: StringHolder(std::string newMember) : member(std::move(newMember)) {} StringHolder(const StringHolder &old) : member(old.member) {} StringHolder(StringHolder &&old) : member(std::move(old.member)) {} }; StringHolder oldHold = std::string("I am a string that will probably be heap-allocated."); StringHolder newHold(oldHold);
This time we now have a class with the constructor copy and move. How many copies of the string do we get?
Two. Of course, these are two. We have oldHold and newHold , each with a copy of the string.
But if we did this:
StringHolder oldHold = std::string("I am a string that will probably be heap-allocated."); StringHolder newHold(std::move(oldHold));
Then again there will be only one copy of the line lying around.
That is why movement is important. That's why it matters: it reduces the number of copies of things you may need to keep them lying.
Why is this not my copy constructor called
Your copy constructor was not called because it was deleted. It does an optimization of the return values. Disabling optimization will not help, because most compilers will still refuse. There is no reason not to do this when elite is possible.
For return values โโof a function, motion is important in cases where an exception is not possible.