Home C++ Introduction Decisions Loops Input/Output Functions Stack and Heap References Arrays Searching and Sorting Recursion Pointers Character and Strings Structures Classes Inheritance Exceptions Templatess STL Modern C++ Misc Books ----

Move Semantics


Contents

RValue Reference

We are familiar with lvalue references . We also have rvalue references. A lvalue reference is initialized with a lvalue variable. However a rvalue reference must be initialized with a rvalue. Every type can have a lvalue and a rvalue. Even a rvalue referemce !

File: ref1.cpp
#include <iostream>


using namespace std ;

template <typename T>
constexpr bool is_lvalue(T&&) {
  return std::is_lvalue_reference<T>{};
}

//LVALUE REFERENCE
void function1LValueRef( int& a1 )
{
   cout << "function1: " << a1 << endl ;
}

//RVALUE REFERENCE
void function2RValueRef( int&& a1 )
{
  //It will always be a lvalue because a1 is a name. However
  //when the function is called it must pass in a rvalue.
  cout << "Is a1 a lvalue: " << is_lvalue( a1 ) << endl ;
  //a1 = 25 ;
}

int main()
{
   int x1 = 10 ;    int y1 = 30 ;
   cout << boolalpha;

   //Need to initialize rvalue references
   int&& x1R = 20 ;
   int&& x2R = 30 ;
   int& x1L = x1  ;

   //compiler error
   //A rvalue reference must be initialized
   // int&& x3R  ;
   //Can only initialize it with a temporary object
   //  int&& x3R = x1  ;

   //can reassign values to r reference
   //Am only changing the value. The x1R is not pointing to
   //something else.
   x1R = x1 ;

   //Can initialize l-value reference with r-value
   //reference. Anything that has a name is l-value.
   int& x3R = x1R ;

   //cannot do
   //At the point of definition. It needs a lvalue
   //int& x2L = 25 ;
   //can do
   const int& x2L = 20 ;

   //int&& x4R = x1L ;
   //Cannot assign lvalue to rvalue reference.

   function1LValueRef( x1 ) ;
   function2RValueRef(  20 ) ;

   // The value of x1 is being assigned to x1R. x1R is not
   // pointing to a different location.
   x1R = x1 ;
   x1R = 40 ;
   //Changes the value of x1R

   //Not a reassignment of the lvalue reference.
   //changes the value of x1 because x1L "points" to x1 .
   x1L = y1 ;

   cout << "x1R:" << x1R << endl ;

   //x1R's value is being assigned to x1.
   x1 = x1R ;
   cout << "x1:" << x1 << endl ;

   //function 2 is r reference
   //Compiler error r-value reference expects a constant
   //ref1.cpp:88:27: error: cannot bind rvalue reference of type ‘int&&’ to lvalue of type ‘int’
   //  function2RValueRef(  x1  ) ;

    //ok move converts a l value to a r value
   function2RValueRef( move(x1)  ) ;

    //x1R is a name so it's a l value at this point
   function1LValueRef( x1R ) ;
    cout << "Is x1R a lvalue: " << is_lvalue( x1R ) << endl ;
    cout << "Is 12 a lvalue: " << is_lvalue( 12 ) << endl ;

    function2RValueRef(  move(x1R)   ) ;
    cout << "x1R value: " << x1R << endl ;
    //compiler error even though x1R is rvalue !!!
    //But it has a name so now is lvalue !!
    // function2RValueRef(  x1R   ) ;
     function2RValueRef( 21 ) ;

     int x2 = 5 ;
     cout << x2 << " " << move(x2) << endl ;





   return 0 ;
}

The key takeaway from the above code is that a rvalue reference is
declared as:

  int&& x2R = 20 ;

We can only assign a temporary value to it at this point. The
"int&&" is a type. It's lvalue is "x2R" and it's rvalue from the
above statement is "20" . Similarly in the function declaration:

//RVALUE REFERENCE
void function2RValueRef( int&& a1 )
{
  //Since a1 is a name it is a lvalue at this point inside the
  //function but the function can only take rvalues.
  cout << "Is a1 a lvalue: " << is_lvalue( a1 ) << endl ;
  //a1 = 25 ;
}

When we call this function we need to supply a rvalue. So
we can call it like this:

function2RValueRef(  20 ) ;

However we cannot do:

int&& x2R = 20 ;
function2RValueRef(  x2R   ) ;

Remember "x2R" is a name and therefore a lvalue and we
cannot initialize a rvalue reference to a lvalue. This
is the probably the most confusing part of rvalue references.

The rvalue and lvalue are sort of properties of the object and we
can change a variable from a lvalue to a rvalue with the std function
"move" and by doing this we can call our function.

function2RValueRef(  move(x2R)   ) ;

Move Function

We can declare an integer such as:

int x1 = 40 ;

The compiler at this stage knows that "x1"
is a lvalue and "40" is a rvalue. However no
extra information is being stored with x1 as
far as lvalue or rvalue. The compiler is using
the lvalue or rvalue to make sure the rules are
followed and the correct function is associated
with the function call.

File: move1.cpp
#include <iostream>
#include <type_traits>
#include <iostream>
using namespace std ;


template<typename T>
typename remove_reference<T>::type&& move_mine(T&& param) noexcept
{
    return static_cast<typename remove_reference<T>::type&&>(param);
}

void f1( int x1 )
{
  cout << "void f1( int x1 )" << endl ;
}

void f1( float x1 )
{
  cout << "void f1( float x1 )" << endl ;
}

float convertIntToFloat(  int a1 )
{
    return float(a1) ;
}

void f2( int& x1 )
{
  cout << "void f2( int& x1 )" << endl ;
}

void f2( int&& x1 )
{
  cout << "void f2( int&& x1 )" << endl ;
}



int main()
{
    int x1 = 10 ;
    int& ref_x1 = x1 ;

    f1( x1 ) ;
    f1 ( convertIntToFloat(x1 ) ) ;

    f2( x1 ) ;
    f2( move_mine( x1 ) ) ;


    return 0;
}
$ g++ move1.cpp ; ./a.exe
void f1( int x1 )
void f1( float x1 )
void f2( int& x1 )
void f2( int&& x1 )

Exercise


1) The following program does not compile. Fix the errors.
File: ref1_ex.cpp
#include <iostream>


using namespace std ;


int main()
{
   int x1 = 10 ;    int y1 = 30 ;

   //Need to initialize rvalue references
   int&& x1R  ;
   int&& x2R  ;


   //Have it "point" to x1
   int& x1L  ;

   //Initialize x3R with RValue
   int&& x3R = x1  ;

   x3R = x1 ;

   return 0 ;
}


1) The following program does not compile. Explain the problem.
File: ref2_ex.cpp
#include <iostream>


using namespace std ;

void f1( int x1 )
{
    cout << x1 << endl ;
}

void f1( int& x1 )
{
    cout << x1 << endl ;
}

void f1( int&& x1 )
{
    cout << x1 << endl ;
}


int main()
{
   int x1 = 10 ;    int y1 = 30 ;
   f1( x1 ) ;
   f1( 15 ) ;

   return 0 ;
}


3) Explain the output of the following program.
File: ref3_ex.cpp
#include <iostream>


using namespace std ;


int main()
{
   int x1 = 10 ;    int y1 = 30 ;

   //Need to initialize rvalue references
   int&& x1R  = 10 ;
   int&& x2R  = 11 ;


   //Have it "point" to x1
   int& x1L = x1 ;
   x1 = 12 ;
   x1R = x1L ;

   //Explain the output
   cout << "x1: " << x1 << endl ;
   cout << "x1L: " << x1L << endl ;
   cout << "x1R: " << x1R << endl ;

return 0 ;
}