R-Values were introduced in C++11. Now, in C++, expressions can be categorized as L-Values or R-Values, and understanding the differences between L-Value and R-Value is important when dealing with assignments, function returns, and references. In this tutorial, we will discuss L-Values and R-Values in detail, and this will act as a foundation for move semantics in upcoming tutorials.
What is an L-Value in C++11? #
L-Values (left values) refer to locations in memory that can be identified and accessed. They persist beyond a single expression, meaning you can assign new values to them. In simple terms, an L-Value is something that has a name and an address, like variables.
For example,
int num = 22;
In this expression, the variable num
is an l-value. Now, how do we identify what is an l-value in an expression? For this, we have a set of properties, and items that adhere to these properties in an expression can be identified as l-values.
Properties of L-Values: #
- L-Value has a persistent memory location (i.e., can be referenced repeatedly).
- L-Value can appear on the left-hand side or right-hand side of an assignment.
- L-Value can be assigned a new value.
Let’s understand these properties of L-Value with examples,
Example of L-Value: #
// x is an L-value
int x = 10;
In this example, x
is an L-value because it refers to a specific memory location that holds an integer. We can also modify L-Values like this:
// Valid, as L-values can be
// assigned new values
x = 20;
We can assign 20
to x
without issue because it is an L-Value and therefore a modifiable object.
Similarly, the variable y
is also an L-Value.
// y is an L-value
int y = 20;
// Both 'x' and 'y' are L-values
x = y;
In the expression x = y
, both x
and y
are L-Values because:
- The variable
x
is on the left-hand side of the assignment. Sincex
represents a specific memory location that can hold data, it qualifies as an L-Value. - The variable
y
is on the right-hand side of the assignment. The value ofy
is used to assign tox
. Here,y
is also an L-Value because it refers to a memory location, and we can retrieve its value for assignment.
Now, I hope we understand what exactly L-Values are in expressions. Next, we will discuss R-Values.
What is an R-Value in C++11? #
R-Values are temporary and do not have a persistent memory address. This means you cannot take the address of an R-Value.
For example,
int num = 22;
In this expression, the 22
is an R-Value.
Basically, R-Values (right values) do not persist beyond the expression that uses them. They are typically literals or temporary objects created by expressions, which means they do not have a specific memory address you can refer to later.
We have a set of properties, and items that adhere to these properties in an expression can be identified as R-Values.
Properties of R-Values in C++11 #
- R-Value does not have a persistent memory location (no address you can take).
- R-Value can only appear on the right-hand side of an assignment.
- R-Value cannot be assigned a new value (they are “read-only” in the context of expressions).
Examples of R-Values in C++11 #
Let’s understand R-Values with the help of some examples. In each example, we will discuss one of the properties of R-Values.
Example 1: R-Values are Temporary #
R-Values do not have a persistent memory location (no address you can take)
// Here `10` is R-Value and 'x' is L-Value
int x = 10;
// OK, 'x' is a L-value and has an address
int* ptr = &x;
// ERROR: Cannot take the address of a R-value (literal)
int* ptr2 = &10; // Error
Here, x
in the expression int x = 10;
is an L-Value with a memory location, so we can take its address and assign a value to it. However, 10
in the expression int x = 10;
is an R-Value (a literal), and trying to take the address of 10
results in a compilation error.
// ERROR: Cannot take the address of a R-value (literal)
int* ptr2 = &10;
Example 2: R-Values on RHS Only #
R-Values can only appear on the right-hand side of an assignment
R-Values can only appear on the right-hand side (RHS) of an assignment, as they represent temporary values or literals. They cannot be on the left-hand side because they don’t represent a modifiable location in memory.
// Here `20` is R-Value and 'x' is L-Value
int x = 20;
// OK: 10 is an R-value, can appear on the RHS
x = 10;
// ERROR: R-value '10' cannot be on the LHS
10 = x;
In the code above:
- The expression
x = 10;
is valid because the R-Value10
appears on the RHS, and its value is assigned to the L-Valuex
. - The expression
10 = x;
is invalid because10
is an R-Value, and R-Values cannot appear on the LHS of an assignment (i.e., we can’t assign a value to a literal).
Example 3: R-Values are “read-only” #
R-Values are temporary and cannot be assigned a new value. They are “read-only” in expressions, meaning you cannot modify them directly. For example,"
// Here `5` is R-Value and 'x' is L-Value
int x = 5;
// Here `10` is R-Value and 'y' is L-Value
int y = 10;
// ERROR: The result of 'x + y' is an R-value,
// cannot be assigned a value
x + y = 15;
In this example:
- The expression
x + y
produces an R-Value (a temporary result of the addition), and you cannot assign a new value to the result of an expression because it is read-only.
So, basically:
- R-Values are temporary values that do not have a persistent memory location (so you can’t take their address).
- R-Values can only appear on the right-hand side of an assignment (they can’t be assigned to).
- R-Values cannot be assigned a new value, as they are considered read-only in expressions."
L-Values vs. R-Values - Explained #
The key differences between L-Values and R-Values are:
- L-Values are memory locations that persist beyond the expression, and you can take their address (i.e., the
&
operator works).- R-Values are temporary values that exist only within the context of an expression and cannot have their address taken.
For example, here we have both L-Values and R-Values:
// 'a' is an L-value, '10' is an R-value
int a = 10;
// 'b' is an L-value, 'a' is also an L-value here, assigned to 'b'
int b = a;
In both the expressions above:
a
andb
are L-Values because they both have memory locations that persist.- The number
10
is an R-Value because it’s a literal that exists only for the moment of assignment.
Similarly, when we return temporary objects from a function, that is also an R-Value. For example,
std::string getName()
{
return "Mark"; // "Mark" is an R-value
}
// 'getName()' returns an R-value (temporary)
// But variable 'name' is an L-Value
std::string name = getName();
In this example, "Mark"
is an R-Value because it’s a temporary string literal created when the function getName()
is called. The result of getName()
is also an R-Value. However, the variable name
is an L-Value since it’s a variable that persists in memory.
Summary #
In this tutorial, we learned about L-Values and R-Values:
L-Values:
- Can appear on the left or right side of an assignment.
- Have a persistent memory address.
- Can be referenced or modified.
R-Values:
- Can only appear on the right side of an assignment.
- Are temporary values with no address that can be referenced.
- Are typically used for move semantics in C++11 and later.
In upcoming articles, we will learn about R-Value references and move semantics with examples, and we will also show how using R-Value references can optimize the performance of applications.