Lvalue and Rvalue references in C++

Lvalue and Rvalue references in C++
Lvalue and Rvalue references in C++

First of all, let’s be faraway from the formal definitions of lvalue and rvalue. In C++ an lvalue is some things that point to a selected memory location. On the opposite hand, an rvalue is some things that do not point anywhere.

Generally, Rvalues are temporary, while lvalues live an extended life since they exist as variables. It is also fun to consider lvalues as containers and rvalues as things contained within the containers. Without a container, they might expire.

Let me show you some examples.
int x = 666; // ok

Here 666 is an rvalue; variety (technically a literal constant) has no specific memory address, apart from some temporary register while the program is running. That number is assigned to x, which may be a variable. A variable features a specific memory location, so its an lvalue. C++ states that an assignment requires an lvalue as its left operand: this is often perfectly legal.

Then with x, which is an lvalue, you’ll do stuff like that:
int* y = &x; // ok
Here I’m taking the address of x and assigning it to y, through the address-of operator &. It takes an lvalue argument value and outputs an rvalue.

The Lvalue refers to a modifiable object in c++ that can be either left or right side of the assignment operator.
The Rvalue refers to a value stored at an address in the memory. It can appear only on the right-hand side of the assignment operator.

Examples:

/* Example – 1 */
int var; // var is an object of type int
var = 1;
// Here var is lvalue and Rvalue is ‘1’
1=var;
// Invalid expression as 1 is lvalue that can’t be in the left

/* Example 2 */
int z;
z=1;
int m;
m=z; // Valid

A reference is another name to an existing variable. It uses the ‘&’ sign before the variable name. References vs Pointers: References and pointers are almost times confused. Here are three points to keep in mind regarding these:

  • You cannot have NULL references, you want to always be ready to assume that a reference is connected to a legitimate piece of storage.
  • Once a reference is initialised to an object, it can’t be changed to ask another object. Pointers are often pointed to a different object at any time.
  • A reference must be initialised when it’s created. Pointers are often initialised at any time.

Creating References in C++
Assume a variable name as a label attached to its location in memory. It is same as you have a registered name in college and nickname at home. Therefore, you can access the value using its original name or its reference name.

Example:
int i = 17;
Creating a reference variable for I as follows.
int& r = i;
Read the & in these declarations as reference. Thus, “r” is another name reference initialized to “i” and read the second declaration as “s is a double reference initialised to “d”.

Example 1:

include<iostream> using namespace std;
int main () {
// declare simple variables
int i;
double d;
// declare reference variables
int& r = i;
double& s = d;
i = 5;
cout << “Value of i : ” << i << endl;
cout << “Value of i reference : ” << r << endl;
d = 11.7;
cout << “Value of d : ” << d << endl;
cout << “Value of d reference : ” << s << endl;
return 0;
}
When the above code is compiled together and executed, it produces the following result −
Value of i: 5
Value of i reference: 5
Value of d: 11.7
Value of d reference: 11.7

Example 2:
int s = 10;

// Declaring lvalue reference
int& l_ref = s;

// Declaring rvalue reference
int&& r_ref = 20;

–> Example for lvalue and rvalue reference
// C++ program to illustrate the
// lvalue and rvalue

include<iostream>

using namespace std;
int main()
{
// Declaring the variable
int a{ 10 };
// Declaring reference to
// already created variable
int& b = a;
// On comparing the variable and address
// the result will come same
// as both are pointing to same location
cout << (&a == &b) << endl;
return 0;
}

Explanation:

  • The code outputs ‘true’ as b is an lvalue reference to a.
  • Both of them are pointing to the same memory locations. b is an alternative name to the memory assigned to a.

Important properties of rvalue references:

  • Rvalue references extend the life of the object to which they are referred(assigned).
  • Non-const rvalue allows for changing the value.

Note: lvalue references can be assigned to Rvalue references but not vice versa.

Example to illustrate lvalue and rvalue:

include<iostream>

using namespace std;
int main()
{
int a = 10;
// Declaring lvalue reference
// (i.e variable a)
int& lref = a;
// Declaring rvalue reference
int&& rref = 20;
// Print the values
cout << “lref = ” << lref << endl;
cout << “rref = ” << rref << endl;
//changing the value of lref
lref = 30;
// Changing the value of rref
rref = 40;
cout << “lref = ” << lref << endl;
cout << “rref = ” << rref << endl;
// Error – The lvalue references cant be assigned to rvalue references
// int &&ref = a;
return 0;
}

Output:
lref = 10
rref = 20
lref = 30
rref = 40

Uses of lvalue references:

  • Lvalues can be used as an alias to an existing object.
  • They can also be used in implementing pass by reference semantics.

Example:

include<iostream>

using namespace std;
// references of the parameter passed to the function swap
void swap(int& x, int& y)
{
int temp = x;
x = y;
y = temp;
}
int main()
{
// initial values
int a{ 10 }, b{ 20 };
cout << “a = ” << a << ” b = ” << b << endl; // Function call –>Call by Reference
swap(a, b);
// Print the swapped values
cout << “a = ” << a
<< ” b = ” << b << endl;
return 0;
}

Output:
a = 10 b = 20
a = 20 b = 10

Uses of Rvalue references:

  • They are used in working with the move constructor and move assignment.
  • cannot bind non-const lvalue reference of type ‘int&‘ to an rvalue of type ‘int’.
  • cannot bind rvalue references of type ‘int&&‘ to an lvalue of type ‘int’.

Examples:
Program 1

include<iostream>

using namespace std;
// lvalue reference to the lvalue
// passed as the parameter
void printReferenceValue(int& x)
{
cout << x << endl;
}

int main()
{
// initial value
int a{ 10 };
// Function call is made lvalue & can
// be assigned to lvalue reference
printReferenceValue(a);
return 0;
}

Output:
10

Program 2

include<iostream>

using namespace std;
// Declaring rvalue reference to the
// rvalue passed as the parameter
void printReferenceValue(int&& x)
{
cout << x << endl;
}
// Driver Code
int main()
{
// Given value a
int a{ 10 };
// Works fine as the function is
// called with rvalue
printReferenceValue(100);
return 0;
}

Output:
100

To explore more topics on C++, click here.

By Mansi Agrawal