Value Categories
There are three types of value in C++: lvalue, xvalue, and prvalue. We’ll explore these three types in this article.
The Has
Property
Naturally, when we define a variable in C++, that variable holds a value. For example, the following C++ statement defines a variable named a
.
int a{ 100 };
This C++ statement corresponds to the assembly instruction below:
push dword 100
The above x86 assembly instruction will be assembled into the following machine code, and we can see that the integer 100
is represented in its hexadecimal form 64
:
6A66 0064
Obviously, a
as a variable, is actually an alias for a block of memory in the program stack, and we have full control over this block of memory, meaning we can either read from or write to this memory block. In such cases, we say a
has an entity, and this entity is where the value 100
will be stored.
However, not all values are stored in memory blocks we can have full control over. Before 100
, the integer literal in the above statement, are stored in the memory block aliased by a
, already exists in the compiled code as an inherent part of the x86 instruction 6A66 0064
. Even though this instruction is stored in memory when the operating system loads the program, we cannot change the hexadecimal 64
to some other value since the operating system forbids us from writing to .text
section, i.e., the memory section where program instructions is placed. Then, we can conclude that we cannot change literals present in our C++ program. We cannot write something like:
100 = 101; // error: assign 101 to literal 100
Then, we say that the literal 100
does not have an entity.
The Move
Property
Let’s delve into the above example. a
is bound to a block of memory which we have full control over. On this basis, we say a
has that entity. As programmers, we cannot deprive a
of its entity, i.e., delete a
and binds its associated memory block to another symbol. Therefore, we say a
’s memory cannot be moved. We often say that a
is non-moveable for simplicity.
There do exist cases in which we can move entities refered by a symbol, of which the simplest case is literals. Since literals does not have associated entities and thus cannot be modified, we can “move” them to anywhere. Such “move” is more like “copy” rather than change an object’s place. Anyone can claim that they get the ownership of a literal as long as they promise not to change their value. Some compilers may allow us to take the ownership of a literal without such promise, but will give us an error. If we try to change that literal through this problematic owership claim, the operating system will have our program crashed at run-time.
const char* s1 = "hello"; // ok, promise not to change a literal's value
char* s2 = "hello"; // warning: cannot assign const char* to char*
[0] = "H"; // will case a runtime error s2
Another case in which we can move is when the C++ runtime creates temporary objects. Different from literals, such temporary objects are usually moveable.
std::string s3 = "Hello";
+ "World" = "hello, world"; s3
The above example first constructs a string object s3
, then we concatenate s3
with a string literal "World"
, during which a temporary string object containing value "HelloWorld"
will be created, i.e., such concatenation is equivalent to the following code:
std::string temp = "HelloWorld";
= "hello, world"; temp
Obviously, temp
as a temporary object, will be held by nobody after the concatenation and assignment operations complete and will be destructed automatically. An natural idea comes that we can take the ownership of such a will-be destructed object, and such idea is the core of move semantics introduced in C++ 11.
Value Categories
Based on “Has” and “Move” property of a value, we can classify C++ values into three categoried.
Has | Move | Category | Example |
---|---|---|---|
yes | no | lvalue | Ordinary variables |
yes | yes | xvalue | Temporary Unnamed Objects |
no | yes | prvalue | Literals |
Since both lvalues and xvalues have associated entities and thus can be modified, people define another more gneralized category — glvalue (general lvalue) to refer to any value that has an associated entity. Likewise, since both xvalues and prvalues are held by nobody, people define a category named rvalue to refer to any moveable value.