In this chapter we shall learn about:
1. what are lvalue and rvalue.
2. What are reference variables?
3. what are rvalue reference.
4. Example for rvalue reference.
5. Move constructor
1. What are lvalue and rvalue?
The value on the left side of assignment operator is lvalue.
The value on the right side of assignment operator is rvalue. “rvalue” is a constant value that doesn’t have any memory location.
It will be stored in tempruary memory and won’t exist on the next line of code.
Example:
int a;
a = 10;
In the statement “a = 10;”
a is lvalue
10 is rvalue
After the line “a = 10”, the rvalue i.e “10” will not be having any memory.
Sometimes a lvalue can also be rvalue. But a rvalue cannot be lvalue.
Example:
int a = 10;
int b;
b = a;
The statement “b = a”; Here ‘a’ is acting as rvalue eventhough it is an lvalue.
But the statement “20 = a” is wrong. Because ’20’ is rvalue and it cannot become lvalue.
2. What are reference variables?
A reference variable is used as an alias to already existing variable.
Example:
int i = 10;
int &j = i;
Now both i and j will have the value 10 and point to the same memory location.
Now, can you do something like below?
int &j = 10;
No, we cannot do. OYu cannot create a reference to a constant.
But you can do
const int &j = 10;
3. Now what is rvalue reference?
Any rvalue reference is represented as ‘&&’ double ampersand.
Then below line is valid in C++11
int &&j = 10;
You can also increment ‘j’ value by “j++”;
4. Example to show rvalue reference:
#include<iostream>
//for more C++ tutorial visit www.ProDeveloperTutorial.com
using namespace std;
void fun(int &num)
{
cout<<"Normal function"<<endl;
}
void fun(int &&num)
{
cout<<"rvalue reference function"<<endl;
}
int main()
{
int i = 10;
fun(i);
fun(20);
return 0;
}
Output:
Normal function rvalue reference function
So this is how the compiler will call the rvalue reference function.
All this is good untill now.
But what is the use of rvalue reference? Why has C++ 11 has this feature?
Consider the example below:
In the below example we have taken a int pointer. Then in the constructor we are creating a new instance of ptr and in the destructor we are deleting it.
Then we have a copy constructor. We do a deep copy. Here we do a ownership transfer.
#include<iostream>
#include<vector> //for more C++ tutorial visit www.ProDeveloperTutorial.com
using namespace std;
class A
{
public:
int *ptr;
//default constructor
A()
{
cout<<"In default constructor"<<endl;
ptr = new int;
}
//copy constructor
A(const A& a1)
{
cout<<"In copy constructor"<<endl;
this->ptr = new int;
*this->ptr = *a1.ptr;
}
//destructor
~A()
{
cout<<"In destructor"<<endl;
delete ptr;
}
};
void fun(int &&num)
{
cout<<"rvalue reference function"<<endl;
}
int main()
{
//create a vector variable
vector<A> v1;
v1.push_back(A());
return 0;
}
Output:
In default constructor In copy constructor In destructor In destructor
So in the above o/p we can see that, when we are pushing an object of class A in to the vector, first default constructor is called, and then copy constructor is called.
Basically we do 2 allocation, for temporary by calling default constructor and when we call the copy constructor.
If we do this push_back 100’s of time, this will cause performance issue.
Because of this move constructor is created.
In move constructor we transfer the ownership.
we create one more function as below:
A(A&& a1){ this->ptr = a1.ptr; a1.ptr = nullptr; }
Update the program as below:
#include<iostream> #include<vector> //for more C++ tutorial visit www.ProDeveloperTutorial.com using namespace std; class A { public: int *ptr; //default constructor A() { cout<<"In default constructor"<<endl; ptr = new int; } //copy constructor A(const A& a1) { cout<<"In copy constructor"<<endl; this->ptr = new int; *this->ptr = *a1.ptr; } //destructor ~A() { cout<<"In destructor"<<endl; delete ptr; } A(A&& a1){ cout<<"In move constructor"<<endl; this->ptr = a1.ptr; a1.ptr = nullptr; } }; void fun(int &&num) { cout<<"rvalue reference function"<<endl; } int main() { //create a vector variable vector<A> v1; v1.push_back(A()); return 0; }
Check the o/p:
In default constructor In move constructor In destructor In destructor
Here it will call move constructor instead of copy constructor.
with the help of move, we avoided copy constructor.