Now that we know what move semantics are,
let's expand our perspective on value categories.
Until now, we learned about two of them, lvalues and prvalues.
We know that lvalues have identity and prvalues don't.
But now, let's also add another property to differentiate them by,
the ability to move.
Lvalues are not movable because they are not temporary,
they have an identity and we usually use values with an identity because we
want to be able to access them for a longer period of time.
If the move constructor stole all of the values from an object with an identity,
we could get in a situation where we want to access that object again later,
but we can't because it's empty; however,
prvalues can definitely be considered as movable because they are temporary.
We don't care what happens to them.
They don't even have an identity, so we can't use them later in the code anyway.
So if prvalues are movable and this expression returns a prvalue,
then this statement should invoke a move constructor,
but as we saw in the previous lesson, it doesn't.
This is because the compiler chooses to implement a type of
return value optimization known as copy elision.
That basically means that the value returned from a function is
directly assigned to this new object identifier.
So instead of passing this prvalue to a move constructor,
compiler will see the result of this expression like this.
If you saw this statement,
you would immediately know that this part allocates space in memory and
then this part assigns a name to that allocated space.
Move constructor is efficient,
but it is even more efficient to not use any constructor at all.
This temporary object is already constructed,
so let's just give it a name and move on.
Back in the old days, and when I say old,
I mean like 2016,
the compiler would have an option to implement this copy
elision if it sees it fit; however,
since C++ 17 copy elision is mandatory in some situations,
and this situation is one of them.
When you initialize an object with prvalue in its declaration,
compiler will use copy elision to skip the move constructor.
But I really want to use the move constructor,
so I will pass this expression to the move function.
Before discussing this function, I first want to show you how it works.
We need to include the utility library because that
is where the function is defined.
Okay, let's recompile the program now.
And it seems that the move constructor was used this time.
So what does this move function actually do?
In essence,
move function will take this argument and return an
rvalue reference to that argument.
So,
the return value of this function is an rvalue
reference to the temporary subject, which is the result of this expression.
Rvalue references returned from a function belong to the xvalue category.
Xvalues are kind of like a combination of lvalues and prvalues.
They are movable like prvalues, but they can also have an identity like lvalues.
These are the three main value categories,
but there are also two super categories.
Super categories represent these two properties, movability and identity.
Instead of saying that a value has an identity, we can say that it's a glvalue.
And instead of saying that the value is movable, we can say that it's an rvalue.
These words are like aliases for identity and moveability, and
expanded they mean global left value and right value.
Lvalues are also glvalues and prvalues are also rvalues
since these are super categories.
But since xvalues are both movable and have an identity,
they can be considered as both glvalues and rvalues.
And that's it.
Now you know what all of the five value categories mean; however,
we still didn't really clarify the purpose of these xvalues,
so let's do that.
As I said, this std::move will return an rvalue reference, which is an xvalue.
Since copy elision only happens with prvalues,
compiler will avoid doing this optimization.
But since xvalues are movable that means that they are also rvalues,
and we know that rvalues will always be sent to the overloaded
functions, which accept rvalue references.
This is why we call it an rvalue reference because it's accepting both
prvalues and also xvalues; however, this use case is kind of stupid
because we prefer copy elision over move constructor.
You would rarely use this move function to turn a prvalue into
an xvalue because they are both rvalues.
The biggest reason why this function exists is to turn lvalues into xvalues.
For example,
what if I wanted to initialize the anzu object with just
this chicken object? Since chicken is an lvalue, this
would call the copy constructor.
But what if for some reason I decided that I don't
need this chicken subject anymore.
I needed it in the previous part of the code, but now I know that after this
statement chicken will just wait until it runs out of scope.
If that's the case,
why don't I just steal all of the data from it and pretend that this object
is temporary, just like on prvalue? By passing this lvalue to the move
function, I'm turning this lvalue into an xvalue.
So in this case, we are moving data from an object that has an identity.
This is why we say that xvalues can have an identity.
Since rvalue references don't usually point to lvalues, the move
constructor will think that this rvalue reference points to a
movable object like a temporary prvalue.
Even though this is not the case here,
we decided that this Chicken should be movable.
So by using the move function we can invoke the move constructor on lvalues.
Xvalue stands for expiring value because the object
they represent is about to expire, it will soon serve no purpose.
Now we know all of the value categories,
but we didn't see any specific purpose for this move constructor.
We will get back to this class in the next module,
where you will see how move constructors can be really
useful for improving performance.