rvalue
- lvalue, an expression that may appear on the left or on the right hand side of an assignment, l stand for location, has physical memory allocated
- rvalue, an expression that can only appear on the right hand side of an assignment, r stand for read, not able to reference a memory location
If it has a name, then it is an lvalue. Otherwise, it is an rvalue.
lvalue reference and ravlue reference
#include <iostream>
#include <string>
//lvalue reference
void display(std::string &str)
{
std::cout<<"Pass lvalue"<<std::endl;
str = str+" --- lvalue";
std::cout<<str<<std::endl;
}
//rvalue reference
void display(std::string && str)
{
std::cout<<"Pass rvalue"<<std::endl;
str = str + " --- rvalue";
std::cout<<str<<std::endl;
}
std::string getStr()
{
return "Get String ...";
}
int main(int argc, char *argv[])
{
std::string str = "Hello";
//call lvalue reference
display(str);
std::cout<<"Str: "<<str<<std::endl;
//call rvalue reference
display(std::move(str));
std::cout<<"Str: "<<str<<std::endl;
//call rvalue reference
display(getStr());
//call rvalue reference
display("Hello World!");
return 0;
}
- Passing variable to a rvalue reference function performs like passing variables to a lvalue reference function. Both them can make changes on the value of variables
- It is alright to overload any function in this manner, however, in the overwhelming majority of cases, this kind of overload should occur only for copy constructors and assignment operators
Is rvalue immutable in C++?
#ifndef V_H
#define V_H
class V
{
private:
int size;
int *array;
public:
//constructor
V(int s):size(s)
{
std::cout<<"Constructor ..."<<std::endl;
array = new int[size];
for(int i = 0; i < size; i++)
array[i] = 10*i;
std::cout<<"----End Constructor ..."<<std::endl;
}
//copy constructor
V(const V &right):size(right.size), array(size?new int [size]:nullptr)
{
std::cout<<"Copy Constructor ..."<<std::endl;
std::copy(right.array, right.array+size, array);
std::cout<<"----End Copy Constructor ..."<<std::endl;
}
//move constructor
V(V && right):size(0), array(nullptr)
{
std::cout<<"Move Constructor ..."<<std::endl;
size = right.size;
array = right.array;
right.size = 0;
right.array = nullptr;
std::cout<<"----End Move Constructor ..."<<std::endl;
}
int *getAddress() const {return array;}
int getSize() const {return size;}
void display() const
{
for(int i = 0; i < size; i++)
std::cout<<array[i]<<" ";
std::cout<<std::endl;
}
V & time(int n)
{
for(int i = 0; i < size; i++)
array[i] *= n;
return *this;
}
//copy assignment
const V& operator=(const V &right)
{
std::cout<<"Copy Assignment ..."<<size<<std::endl;
if(this != &right)
{
V temp(right);
std::swap(size, temp.size);
std::swap(array, temp.array);
std::cout<<"----End Assignment ..."<<std::endl;
}
return *this;
}
//move assignment
V & operator=(V&& right)
{
std::cout<<"Move Assignment ..."<<std::endl;
if(this != &right)
{
delete [] array;
array = 0;
size = 0;
size = right.size;
array = right.array;
right.size = 0;
right.array = nullptr;
}
std::cout<<"----End Move Assignment ..."<<std::endl;
return *this;
}
~V()
{
std::cout<<"Destructor ..."<<size<<std::endl;
delete array;
array = 0;
size = 0;
}
};
#endif
#include <iostream>
#include "V.h"
//return an object
V getObject()
{
std::cout<<"Inside function ..."<<std::endl;
//call constructor
V v(10);
std::cout<<"Return from function ..."<<std::endl;
return v;
}
//pass by lvalue reference
void byRef(V &v)
{
std::cout<<"-------Pass by lvalue reference-------"<<std::endl;
}
//pass by constant lvalue reference
void byRef(const V &v)
{
std::cout<<"-------Pass by constant lvalue reference-------"<<std::endl;
}
//pass by rvalue reference
void byRef(V &&v)
{
std::cout<<"-------Pass by rvalue reference-------"<<std::endl;
v.time(2);
v.display();
}
void byRef(const V&& v)
{
std::cout<<"-------Pass by constant rvalue reference-------"<<std::endl;
}
int main(int argc, char *argv[])
{
//V *ptr = &getObject();//compile error, rvalue
V v(10);
//pass by lvalue reference
byRef(v);
//pass by rvalue reference
byRef(getObject());
byRef(std::move(getObject()));
byRef(std::move(v));
//immutable and mutable
//int x;
//(x+7) = 7;//compile error
V v2 = getObject().time(10);
std::cout<<"Assign to itself ..."<<std::endl;
v2 = v2.time(2);
std::cout<<"Assign to another object ..."<<std::endl;
v2 = v.time(2);//call copy assignment
v.time(2);
//v2 = std::move(v);//call move assignment
v.display();
v2.display();
return 0;
}
- rvalues of builtin data type is immutable
- rvalue of User Defined data type is mutable
- Accessor functions, set as constant
- Mutator functions, return *this for return and chain calls
- If mutator function return const object, cannot use chain call to call mutator function
- const V& time(int t)
- v.time(2).time(2);
Type Deduction
#include <iostream>
template <class T>
void f(T& param)
{
//param++;
std::cout<<param<<std::endl;
}
int main(int argc, char *argv[])
{
int x = 22;
f(x);//x is int&
const int cx = x;
f(cx);//cx is const int&
const int& rx = x;
f(rx);//rx is const int&
int v = 22;
auto& v1 = v;//v1 is int&
const int cv = v;
auto& v2 = cv;//v2 is const int&
const int& rx = v;
auto& v3 = rx;//v3 is const int
return 0;
}
#include <iostream>
#include <string>
#include <utility>
template <class T>
void display(T &p)
{
std::cout<<"Pass lvalue: "<<p<<std::endl;
}
template <class T>
void display(T &&p)
{
std::cout<<"Pass rvalue: "<<p<<std::endl;
}
int main(int argc, char *argv[])
{
std::string str = "Hello World!";
display(str);//lvalue
display("Hello World!");//lvalue
display(std::forward<std::string>("Hello World!"));//rvalue
display(std::move(str));//rvalue
return 0;
}
TR | R | |
T& | & | T& |
T& | && | T& |
T&& | & | T& |
T&& | && | T&& |
#include <iostream>
#include <string>
template <class T>
void display(T &&p)//universal reference
{
std::cout<<"Universal reference: "<<p<<std::endl;
}
int main(int argc, char *argv[])
{
std::string str = "Hello World!";
display(str);//lvalue
display("Hello World!");//rvalue
display(std::forward<std::string>("Hello World!"));//rvalue
display(std::move(str));//rvalue
return 0;
}
- Do not have to overload function for T& and T&&, define T&& function only with universal reference
- If a variable or parameter is declared to have type T&& for some deduced type, that variable or parameter is a universal reference
- Universal Reference