Is an lvalue any expresion that say, could be to the left of the assignment operator, and a rvalue to the right (whose value cannot be modified)? is that simple? or am I missing something?
On the other hand, in the following code:
CMessage & operator=(CMessage && aMess)
In the book I`m reading, it says that this argument is an rvalue, so, why don`t simply use a regular object, as CMessage & operator=(CMessage aMess) whithout this squeeze thing of && ? what does it mean?
What makes rvalue references a bit difficult to grasp is that when you first look at them, it is not clear what their purpose is or what problems they solve. [...]
Rvalue references solve at least two problems:
* Implementing move semantics
* Perfect forwarding
Ok, but I still don't get something. In the following code:
1 2 3 4 5 6 7 8
CMessage & operator=(CMessage && aMess)
{
cout < < "Move assignment operator function called." < < endl;
delete[] pmessage; // Release memory for left operand
pmessage = aMess.pmessage; // Steal string from rhs object
aMess.pmessage = nullptr; // Null rhs pointer
return *this; // Return a reference to 1st operand
}
The argument is an Rvalue reference from which we steal its allocated memory to avoid copying (thus overloading) the function call. Then we set the pointer member of the Rvalue reference to nullptr, so it is automatically deleted when this object destructor is called, after the function ends. This is ok, I understand.
What I don't understand is why use this thing of &&? wouldn't it be possible to achieve the same by using a plain object? I mean, by using a plain object as (CMessage message) we are not modifying the original argument to the function, since it would be a copy of it, which will also be destroyed after the function ends. It would also be a temporary object as the Rvalue reference.
Unless... the Rvalue reference is not a copy of the original argument, but the argument itself (as a simple reference) avoiding the creation of a temporary object, but which cannot be modified as it is defined as an rvalue.... I think now I`ve got it! Is this the way it works?
Unless... the Rvalue reference is not a copy of the original argument, but the argument itself (as a simple reference) avoiding the creation of a temporary object
Yes
but which cannot be modified as it is defined as an rvalue....
Nope. It is modified as with normal reference. But by creating and passing r-value reference we tell program: "we do not need this object anymore, you can salvage it for any usable parts to avoid costly copy".
Wait a moment, if it is a reference to the original argument, declared as an rvalue for not modifying the original argument... how on earth can it be destroyed when function ends? it must be a copy!! again at the beginning... looking for answers
r-value references are special kind of references which you cannot create accidentally. Because using (passing) r-value reference will break original object (standart says that it is left in unspecified state and trying to use it is undefined behavior). However. It is still an object and his lifetime will continue after r-value reference was taken from him. It is destroyed when it would be normally.
That is main danger with r-references manipulation: you sjould ensure that object will not become undestructible (like leaving hanging pointer in it) and it destruction will not damage other objects (like with leaving pointer to buffer unchanged)
Usefulness of r-value references are that we can us them even when function only supports passing by copy. For example:
1 2 3 4 5 6 7 8 9 10 11
void foo(std::string s);
//...
{
std::string bar = "Hello"
foo(bar); //Copy
foo(std::move(bar)); //We are taking r-value reference (by std::move)
//and passing it to std::string constuctor. As string does have move constructor
//it is called and we are avoiding costly buffer allocation and copy
//we cannot use bar here
} //bar is destroyed there. It is not like there anything left to destroy...
Ok, so it is not a copy, and it is intended for moving the object to avoid allocation costs. I can imagine that it only has to do with assignments. And what happens to the rhs? since the rvalue reference is destroyed in the process?
Is it also destroyed in the process?
Because if I need to do something like
anObject = theOriginalObject;
then, the original would be destroyed... if so, I rather define the assignment operator as
I can imagine that it only has to do with assignments
And move constructors. When you returning by value you invoke move constructor too.
And what happens to the rhs? since the rvalue reference is destroyed in the process?
rhs is an r-value reference. Nothing will happen to object it points to when function ends. However in function itself you made some changes to object it points to making it unsafe to use. So essentually object is brokend beyound repair and only thing you can do is to destroy it ar wait until it will be automaticly destroyed.
Because if I need to do something like
anObject = theOriginalObject;
then, the original would be destroyed...
Nope. You need to explicitely provide an r-value reference to use move semantics. So you should do anObject = std::move(theOriginalObject);
if so, I rather define the assignment operator as
You should have two assigment operators: one taking const reference (a copy assigment) and one taking r-value reference (a move assingment). Same with copy/move constructors.
Move copy is often used when old object will not bee needed. For example when you are keeping an internal buffer and need to expand it (like std::vector). Usually you would allocate new buffer, copy (slow) all objects from old one and destroy old one. If objects stored can be moved, they will be moved instead which is way faster.
Ahhhh! Now I understand! It is much more clear now you've explained with the std::vector example. I can have both assignment operators, and use std::move in case I can/want to dismiss the original
Great!
Now I am happy again! :)
Thank you very much MiiNiPaa!
(Now I better go to sleep, it's 4 o'clock and I have to work...)