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
// 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.