BogoToBogo
  • Home
  • About
  • Big Data
  • Machine Learning
  • AngularJS
  • Python
  • C++
  • go
  • DevOps
  • Kubernetes
  • Algorithms
  • More...
    • Qt 5
    • Linux
    • FFmpeg
    • Matlab
    • Django 1.8
    • Ruby On Rails
    • HTML5 & CSS

C++11/C++14 5. rvalue Reference and Move Semantics - 2020

cplusplus_icon.png




Bookmark and Share





bogotobogo.com site search:



"If you're an experienced C++ programmer and are anything like me, you initially approached C++11 thinking, "Yes, yes, I get it. It's C++, only more so." But as you learned more, you were surprised by the scope of the changes. auto declarations, range-based for loops, lambda expressions, and rvalue references change the face of C++, to say nothing of the new concurrency features. And then there are the idiomatic changes. 0 and typedefs are out, nullptr and alias declarations are in. Enums should now be scoped. Smart pointers are now preferable to built-in ones. Moving objects is normally better than copying them.
- Effective Modern C++ by Scott Meyers





Pass by value

What's the implication of passing a variable by value?

When a function gets its parameter as by value, it does copy. Compiler knows how to copy it. If it's a user defined type, we may need to provide copy constructor, and probably assignment operator as well to be able to do a deep copy. However, copying is expensive. Actually, there are bunch of copying going on when we use STL containers. When an object tossed in by value, since it's a temporary object (rvalue), we're really wasting our precious resources including CPU as well as memory and so on.

So, we need to find a way to avoid such an unnecessary copy: move semantics




rvalue Reference

The primary purpose of introducing an rvalue is to implement move semantics which will be discussed later in this tutorial.


rvalue references enable us to distinguish an lvalue from an rvalue.

The C++11 Standard introduces rvalue references which bind only to rvalues. They are declared with two ampersands rather than one ampersand:

int&& rvalue_ref = 99;

Note that only an lvalue can be assigned to a normal reference, so if we do the following:

int& ref = 9;   

we get an error: "invalid initialization of non-const reference of type int& from an rvalue of type int

We can only do:

int nine = 9;
int& ref = nine;

Here is an example of rvalue reference. We have two functions: one that's taking an lvalue reference and the other one taking an rvalue:

#include <iostream>

void f(int& i) { std::cout << "lvalue ref: " << i << "\n"; }
void f(int&& i) { std::cout << "rvalue ref: " << i << "\n"; }

int main()
{
    int i = 77;
    f(i);    // lvalue ref called
    f(99);   // rvalue ref called

    f(std::move(i));  // rvalue ref called

    return 0;
}

Note that the move(i) is calling a function taking rvalue. This is possible because move() takes an lvalue and converts it into an rvalue.

An expression is an rvalue if it results in a temporary object as shown in the example below.

#include <iostream>

int getValue ()
{
    int ii = 10;
    return ii;
}

int main()
{
    std::cout << getValue();
    return 0;
}

The getValue() is an rvalue.
Note that the value being returned is not a reference to ii, but it's just a temporary value.

In C++0x, we could use a normal or lvalue reference to bind temporary object, but only if it was const:

const int& val = getValue(); // OK
int& val = getValue(); // NOT OK

In C++11, however, the rvalue reference lets us bind a mutable reference to an rvalue, but not an lvalue. In other words, rvalue references are perfect for detecting whether a value is a temporary object or not.

const int&& val = getValue(); // OK
int&& val = getValue(); //  OK

Let's compare both references:

void printReference (const int& value)
{
        cout << value;
}
 
void printReference (int&& value)
{
        cout << value;
}

The printReference() function taking a const lvalue reference will accept any argument whether it be an lvalue or an rvalue. However, the second overloaded printReference*() taking only an rvalue reference.

In other words, by using the rvalue references, we can use function overloading to determine whether function parameters are lvalues or rvalues by having one overloaded function taking an lvalue reference and another taking an rvalue reference. In other words, C++11 introduces a new non-const reference type called an rvalue reference, identified by T&&. This refers to temporaries that are permitted to be modified after they are initialized, which is the cornerstone of move semantics.

#include <iostream>

using namespace std;

void printReference (int& value)
{
        cout << "lvalue: value = " << value << endl;
}
 
void printReference (int&& value)
{
        cout << "rvalue: value = " << value << endl;
}

int getValue ()
{
    int temp_ii = 99;
    return temp_ii;
}

int main()
{ 
	int ii = 11;
	printReference(ii);
	printReference(getValue());  //  printReference(99);
	return 0;
}

With the output:

lvalue: value = 11
rvalue: value = 99

Note that we took out const from the void printReference (int& value) so that it can take only the lvalue.

Now, we have two distinct functions that can be overloaded: one taking only lvalue and the other one only taking rvalue. So, what?

It gives us a way to write more efficient program with less coding!


rvalue summary:

  1. int &&a;: new (C++11) declaration rvalue reference.
  2. non-const lvalue reference binds to an object.
  3. rvalue reference binds to a temporary object which typically will not be used again.




Move semantics

In C++03, there was a costly and unnecessary deep copies that can happen implicitly when objects are passed by value. We can avoid the performance hit due to deep copy by using a rvalue reference.

Now we have a way (overloading functions - taking lvalue or rvalue) to determine if a reference variable refers to a temporary object or to a permanent object. So, how it can be used?

The main usage of rvalue references is to create a move constructor and move assignment operator. A move constructor, like a copy constructor, takes an instance of an object as its argument and creates a new instance from original object. However, the move constructor can avoid memory reallocation because we know it has been provided a temporary object, so rather than copy the fields of the object, we will move them.

In other words, the rvalue references and move semantics allows us to avoid unnecessary copies when working with temporary objects. We do not want to copy the temporary which will go away. So, the resources needed for the temporary objects can be used for other objects.

The rvalues are typically temporary, and they can be modified. If we know that our function parameter is an rvalue, we can use it as a temporary storage, or take its contents without affecting the output of our program. This means that we can just move the contents rather than copy the contents of an rvalue parameter. This saves a lot of memory allocation and provides a lot of scope for optimization for large dynamic structures.

Here is the typical class definition using move semantics:


#include <iostream>
#include <algorithm>

class A
{
public:

    // Simple constructor that initializes the resource.
    explicit A(size_t length)
        : mLength(length), mData(new int[length])
    {
        std::cout << "A(size_t). length = "
		<< mLength << "." << std::endl;
    }

    // Destructor.
    ~A()
    {
	std::cout << "~A(). length = " << mLength << ".";

	if (mData != NULL) {
            std::cout << " Deleting resource.";
	    delete[] mData;  // Delete the resource.
	}

	std::cout << std::endl;
    }

    // Copy constructor.
    A(const A& other)
	    : mLength(other.mLength), mData(new int[other.mLength])
    {
	std::cout << "A(const A&). length = "
		<< other.mLength << ". Copying resource." << std::endl;

	std::copy(other.mData, other.mData + mLength, mData);
    }

    // Copy assignment operator.
    A& operator=(const A& other)
    {
	std::cout << "operator=(const A&). length = "
	         << other.mLength << ". Copying resource." << std::endl;

	if (this != &other;) {
	    delete[] mData;  // Free the existing resource.
	    mLength = other.mLength;
            mData = new int[mLength];
            std::copy(other.mData, other.mData + mLength, mData);
	}
	return *this;
    }

    // Move constructor.
    A(A&& other) : mData(NULL), mLength(0)
    {
        std::cout << "A(A&&). length = " 
             << other.mLength << ". Moving resource.\n";

        // Copy the data pointer and its length from the 
        // source object.
        mData = other.mData;
        mLength = other.mLength;

        // Release the data pointer from the source object so that
        // the destructor does not free the memory multiple times.
        other.mData = NULL;
        other.mLength = 0;
    }

    // Move assignment operator.
    A& operator=(A&& other)
    {
        std::cout << "operator=(A&&). length = " 
             << other.mLength << "." << std::endl;

        if (this != &other;) {
          // Free the existing resource.
          delete[] mData;

          // Copy the data pointer and its length from the 
          // source object.
          mData = other.mData;
          mLength = other.mLength;

          // Release the data pointer from the source object so that
          // the destructor does not free the memory multiple times.
          other.mData = NULL;
          other.mLength = 0;
       }
       return *this;
    }

    // Retrieves the length of the data resource.
    size_t Length() const
    {
        return mLength;
    }

private:
    size_t mLength; // The length of the resource.
    int* mData;     // The resource.
};

#include <vector>

int main()
{
   // Create a vector object and add a few elements to it.
   std::vector<A> v;
   v.push_back(A(25));
   v.push_back(A(75));

   // Insert a new element into the second position of the vector.
   v.insert(v.begin() + 1, A(50));
   return 0;
}

The example above adds two elements to a vector object and then inserts a new element between the two existing elements.




Move Constructor

In its the simplest form, a move constructor looks like this:

A(A&& other) noexcept    // C++11 - specifying non-exception throwing functions
{
  mData =  other.mData;  // shallow copy or referential copy
  other.mData = nullptr;
}

Note that it doesn't allocate any new resources and contents are moved not copied: what was in other moved to a new member, and what was in other disappeared. It pilfers other's resources and then sets other to its default-constructed state. The most critical fact is that there's no additional memory allocation. We just assign an address which only requires couple of machine instructions.

Suppose that address is pointing to an array which has millions of integer. We do not copy the elements. We do not create anything. We just move it. If we use old copy constructor for a class which has an 1-million element array member, we may have that many assignment operations, which is costly. Now, with the move constructor, we can save a lot.

// Move constructor.
A(A&& other) : mData(NULL), mLength(0)
{
    // Copy the data pointer and its length from the 
    // source object.
    mData = other.mData;
    mLength = other.mLength;

    // Release the data pointer from the source object so that
    // the destructor does not free the memory multiple times.
    other.mData = NULL;
    other.mLength = 0;
}

The move constructor is much faster than a copy constructor because it doesn't allocate memory nor does it copy memory blocks.




Move Assignment Operator

A move assignment operator has the following signature:

A& operator=(A&& other) noexcept
{
  mData =  other.mData;
  other.mData = nullptr;
  return *this;
}

Note: non-cppying move assignment and rhs destroyed.

A move assignment operator is similar to a copy constructor except that before pilfering the source object, it releases any resources that its object may own. The move assignment operator takes the following steps:

  1. Release any resources that *this currently owns.
  2. Pilfer other's resource.
  3. Set other to a default state.
  4. Return *this.

Here's a definition for the move assignment operator:

// Move assignment operator.
A& operator=(A&& other)
{
    std::cout << "operator=(A&&). length = " 
             << other.mLength << "." << std::endl;

    if (this != &other;) {
      // Free the existing resource.
      delete[] mData;

      // Copy the data pointer and its length from the 
      // source object.
      mData = other.mData;
      mLength = other.mLength;

      // Release the data pointer from the source object so that
      // the destructor does not free the memory multiple times.
      other.mData = NULL;
      other.mLength = 0;
   }
   return *this;
}



How it works

Since C++11 supports rvalue references, the Standard Library functions such as vector::push_back() now define two overloaded versions: one that takes const T& for lvalue arguments as before, and a new one that takes a parameter of type T&& for rvalue arguments. The code in the main() populates a vector with A objects using two push_back() calls:

std::vector<A> v;
v.push_back(A(25));
v.push_back(A(75));

Both push_back() calls resolve as push_back(T&&) because their arguments are rvalues. The push_back(T&&) moves the resources from the argument into vector's internal A objects using A's move constructor. In C++03, the same program would generate copies of the argument since the copy constructor of A would be called instead.

The push_back(const T&) would have been called if the argument have had an lvalue:

#include <vector>

int main()
{
    std::vector<A> v;
    A aObj(25);       // lvalue
    v.push_back(aObj);  // push_back(const T&)
}

However, we can enforce the selection of push_back(T&&) even in this case by casting an lvalue to an rvalue reference using static_cast:

// calls push_back(T&&)
v.push_back(static_cast<A&&>(aObj));

Alternatively, we can use the new standard function std::move() to serve the same thing:

v.push_back(std::move(aObj));  //calls push_back(T&&)

It may seem that push_back(T&&) is always the best choice because it eliminates unnecessary copies. However, remember that push_back(T&&) empties its argument. If we want the argument to retain its state after a push_back() call, we should stick to copy semantics.



Swapping objects using move()

The following code shows how we can use move() to swap objects:

/* move.cpp */
#include <iostream>
using namespace std;

class A
{
  public:
    // constructor
    explicit A(size_t length)
        : mLength(length), mData(new int[length]) {}

    // move constructor
    A(A&& other)
    {
      mData = other.mData;
      mLength = other.mLength;
      other.mData = nullptr;
      other.mLength = 0;
    }

    // move assignment
    A& operator=(A&& other) noexcept
    {
      mData =  other.mData;
      mLength = other.mLength;
      other.mData = nullptr;
      other.mLength = 0;
      return *this;
    }

    size_t getLength() { return mLength; }

    
    void swap(A& other)
    {
      A temp = move(other);
      other = move(*this);
      *this = move(temp);
    }

    int* get_mData() { return mData; }

  private:
    int *mData;
    size_t mLength;
};

int main()
{
  A a(11), b(22);
  cout << a.getLength() << ' ' << b.getLength() << endl;
  cout << a.get_mData() << ' ' << b.get_mData() << endl;
  swap(a,b);
  cout << a.getLength() << ' ' << b.getLength() << endl;
  cout << a.get_mData() << ' ' << b.get_mData() << endl;
  return 0;
}

Output:

$ g++ -std=c++11 -o move move.cpp
$ ./move
11 22
0x1870010 0x1870050
22 11
0x1870050 0x1870010



C++11/C++14 Thread Tutorials



C++11 1. Creating Threads

C++11 2. Debugging with Visual Studio 2013

C++11 3. Threading with Lambda Function

C++11 4. Rvalue and Lvalue

C++11 5. Move semantics and Rvalue Reference

C++11 5B. Move semantics - more samples for move constructor

C++11 6. Thread with Move Semantics and Rvalue Reference

C++11 7. Threads - Sharing Memory and Mutex

C++11 8. Threads - Race Conditions

C++11 9. Threads - Deadlock

C++11 10. Threads - Condition Variables

C++11 11. Threads - unique futures (std::future<>) and shared futures (std::shared_future<>).

C++11 12. Threads - std::promise





C++11/C++14 New Features



initializer_list

Uniform initialization

Type Inference (auto) and Range-based for loop

The nullptr and strongly typed enumerations

Static assertions and Constructor delegation

override and final

default and delete specifier

constexpr and string literals

Lambda functions and expressions

std::array container

Rvalue and Lvalue (from C++11 Thread tutorial)

Move semantics and Rvalue Reference (from C++11 Thread tutorial)





Ph.D. / Golden Gate Ave, San Francisco / Seoul National Univ / Carnegie Mellon / UC Berkeley / DevOps / Deep Learning / Visualization

YouTubeMy YouTube channel

Sponsor Open Source development activities and free contents for everyone.

Thank you.

- K Hong






Sponsor Open Source development activities and free contents for everyone.

Thank you.

- K Hong






C++ Tutorials

C++ Home

Algorithms & Data Structures in C++ ...

Application (UI) - using Windows Forms (Visual Studio 2013/2012)

auto_ptr

Binary Tree Example Code

Blackjack with Qt

Boost - shared_ptr, weak_ptr, mpl, lambda, etc.

Boost.Asio (Socket Programming - Asynchronous TCP/IP)...

Classes and Structs

Constructor

C++11(C++0x): rvalue references, move constructor, and lambda, etc.

C++ API Testing

C++ Keywords - const, volatile, etc.

Debugging Crash & Memory Leak

Design Patterns in C++ ...

Dynamic Cast Operator

Eclipse CDT / JNI (Java Native Interface) / MinGW

Embedded Systems Programming I - Introduction

Embedded Systems Programming II - gcc ARM Toolchain and Simple Code on Ubuntu and Fedora

Embedded Systems Programming III - Eclipse CDT Plugin for gcc ARM Toolchain

Exceptions

Friend Functions and Friend Classes

fstream: input & output

Function Overloading

Functors (Function Objects) I - Introduction

Functors (Function Objects) II - Converting function to functor

Functors (Function Objects) - General



Git and GitHub Express...

GTest (Google Unit Test) with Visual Studio 2012

Inheritance & Virtual Inheritance (multiple inheritance)

Libraries - Static, Shared (Dynamic)

Linked List Basics

Linked List Examples

make & CMake

make (gnu)

Memory Allocation

Multi-Threaded Programming - Terminology - Semaphore, Mutex, Priority Inversion etc.

Multi-Threaded Programming II - Native Thread for Win32 (A)

Multi-Threaded Programming II - Native Thread for Win32 (B)

Multi-Threaded Programming II - Native Thread for Win32 (C)

Multi-Threaded Programming II - C++ Thread for Win32

Multi-Threaded Programming III - C/C++ Class Thread for Pthreads

MultiThreading/Parallel Programming - IPC

Multi-Threaded Programming with C++11 Part A (start, join(), detach(), and ownership)

Multi-Threaded Programming with C++11 Part B (Sharing Data - mutex, and race conditions, and deadlock)

Multithread Debugging

Object Returning

Object Slicing and Virtual Table

OpenCV with C++

Operator Overloading I

Operator Overloading II - self assignment

Pass by Value vs. Pass by Reference

Pointers

Pointers II - void pointers & arrays

Pointers III - pointer to function & multi-dimensional arrays

Preprocessor - Macro

Private Inheritance

Python & C++ with SIP

(Pseudo)-random numbers in C++

References for Built-in Types

Socket - Server & Client

Socket - Server & Client 2

Socket - Server & Client 3

Socket - Server & Client with Qt (Asynchronous / Multithreading / ThreadPool etc.)

Stack Unwinding

Standard Template Library (STL) I - Vector & List

Standard Template Library (STL) II - Maps

Standard Template Library (STL) II - unordered_map

Standard Template Library (STL) II - Sets

Standard Template Library (STL) III - Iterators

Standard Template Library (STL) IV - Algorithms

Standard Template Library (STL) V - Function Objects

Static Variables and Static Class Members

String

String II - sstream etc.

Taste of Assembly

Templates

Template Specialization

Template Specialization - Traits

Template Implementation & Compiler (.h or .cpp?)

The this Pointer

Type Cast Operators

Upcasting and Downcasting

Virtual Destructor & boost::shared_ptr

Virtual Functions



Programming Questions and Solutions ↓

Strings and Arrays

Linked List

Recursion

Bit Manipulation

Small Programs (string, memory functions etc.)

Math & Probability

Multithreading

140 Questions by Google



Qt 5 EXPRESS...

Win32 DLL ...

Articles On C++

What's new in C++11...

C++11 Threads EXPRESS...

Go Tutorial

OpenCV...








Contact

BogoToBogo
contactus@bogotobogo.com

Follow Bogotobogo

About Us

contactus@bogotobogo.com

YouTubeMy YouTube channel
Pacific Ave, San Francisco, CA 94115

Pacific Ave, San Francisco, CA 94115

Copyright © 2024, bogotobogo
Design: Web Master