Part 4 - lvalue, prvalue, xvalue, glvalue, rvalue
This part is divided into 2 parts - practical part (how to use stuff) and theoritical part (complex stuff behind it). To correctly use modern C++ you DO NOT need to know the complex part. It's more for people that are curios how it works "behind the curtain".
Practice
If you have an object and you want to transfer it's ownership you can instead std::move
it.
Think of it as "moving" objects in memory. It's still cheaper to move than to copy tho.
What can you move? Anything that has "location in memory". For example std::move(4)
doesn't really make sense.
In that case you can think about std::move
as converting a variable into "something" like 4
.
After moving, object "moved from" is left in "valid, but unspecified state" which means - don't touch it.
auto a = 5;
// after doing std::move we cannot touch a
// type of b is "int&&"
auto b = std::move(a);
// not using auto on purpose that you can assign it to a variable
// since it "became something like 4"
int c = b;
Since std::move
can get a bit complicated it's best if you just watch this part of Jason Turner's cppcon talk abotut it.
(While you're at it just watch all of his talks, they're great).
Theory
This part will be really boring, but it's a pathway to many abilities some consider to be unnatural.
If you're brave, you can read the [basic.lval].
This part will oversimplyfy things by a lot. The goal isn't to teach C++ standard - the goal is to understand how to use it. I don't know which explanation will be the most intuitive for the reader, so I'm just putting in all of them.
type | has identity | can be moved from |
---|---|---|
lvalue | YES | NO |
xvalue | YES | YES |
prvalue | NO | YES |
glvalue | xvalue or lvalue | |
rvalue | xvalue or prvalue |
-
gvalue - they really denote to locations (in memory). When you have a variable - it is stored somewhere in memory (go back to part1 if this is confusing).
example:int a
(every variable has a location) -
prvalue - used for initialization of an object
example:4
(yes, just a simple number) - xvalue - glvalues that denote objects that are at the end of their lifetime or are otherwise marked for being able to have their resources reused
- lvalue - gvalue that is not xvalue
- rvalue - prvalue taht is not xvalue
// all assignments - lvalue
a = b;
// name of variable, function or data member - lvalue
std::cin
// literal - rvalue
42
// stuff like this - rvalue
str1 + str2
// rvalue reference to an object - xvalue
a = std::move(x);
std::move
and std::forward
std::move
produces xvalue that identifies it's argument. It's effectively a static_cast
to rvalue reference.
std::forward
passes it's argument along with same value type as it had before. At first you might think it's useless, but when you call a function with the value 4
as an argument, it clearly was a prvalue, but now it became a variable that you can use inside the function, thus having identity and becoming a glvalue. std::forward
avoids that, which will be usefull in part5.