Move Semantics in C++

In C++, to move means to move the contents (of an object) from one place in memory to another. You typically move something pointed to or some acquired resource such as a file handle or a connection (to a database, or a socket, or...).

L- and R-Values

The term lvalue originally referred to an expression that could be the left hand side of an assignment. Correspondingly, an rvalue (although as I recall that term was not used by the C89 standard), was originally just the opposite: an expression that could not be the left hand side of an assignment, but that could only be the right hand side.

C++11 complicated this by adding several more nuanced terms, but let's concentrate on the C++03 meanings. For example, if you have

int x;

then the assignment x = 42 is OK, so x is an lvalue expression.

As a counter-example, the assigment x+0 = 42 is not OK, so x+0 is an rvalue expression.

And so is the expression 2+2, it's an rvalue expression.

So, if the requirement is that your program should include an rvalue, then just write 2+2 or something analogous.

Original C didn't have const. In C++, with const, you have to cannot rely on const for the purpose of making an expression an lvalue or an rvalue. The critical point is then whether the expression guaranteed refers to an object in memory, an object with an address: if so, then the expression is an lvalue.

Thus, a reference type implies an lvalue, because an expression of reference type is necessarily referring to an object with a memory address, i.e. that expression is an lvalue. However, other than references there's no connection between type and lvalue/rvalue. For example, both x and x+0 are expressions of type int, and they do indeed yield the same int value. But the former is an lvalue expression, while the latter is an rvalue expression.

As a general rule, if you can apply the built-in address operator, then it's an lvalue expression, and otherwise it's an rvalue expression.


In C++, an rvalue is an unnamed object or a member of such an object which is not a reference.

Causing Move Semantics with std::move()

...

// Creating an object using Move constructor
Object obj2 = std::move(obj1);

// Creating an object using Move assignment operator
Object obj3;
obj3 = std::move(obj2);

...

Forwarding

Forwarding is the process where one function forwards its parameter to another function.

Perfect Forwarding

With perfect forwarding, the function should receive the same object passed from the function that does the forwarding. In other words, perfect forwarding means we do not just forward objects, we also forward their salient properties, whether they are lvalues or rvalues, const or volatile.

std::forward in Header <utility>

Function std::forward(arg) returns an rvalue reference to arg if arg is not an lvalue reference. If arg is an lvalue reference, the function returns arg without modifying its type.

The need for this function stems from the fact that all named values (such as function parameters) always evaluate as lvalues (even those declared as rvalue references), and this poses difficulties in preserving potential move semantics on template functions that forward arguments to other functions.

These are their twin prototypes:

template <class T> T&& forward (typename remove_reference<T>::type& arg) noexcept;
template <class T> T&& forward (typename remove_reference<T>::type&& arg) noexcept;

Example:

#include <utility>      // std::forward
#include <iostream>     // std::cout

// function with lvalue and rvalue reference overloads:
void overloaded (const int& x) {std::cout << "[lvalue]";}
void overloaded (int&& x) {std::cout << "[rvalue]";}

// function template taking rvalue reference to deduced type:
template <class T> void fn (T&& x) {
  overloaded (x);                   // always an lvalue
  overloaded (std::forward<T>(x));  // rvalue if argument is rvalue
}

int main () {
  int a;

  std::cout << "calling fn with lvalue: ";
  fn (a); // [lvalue][lvalue]
  std::cout << '\n';

  std::cout << "calling fn with rvalue: ";
  fn (0); // [lvalue][rvalue]
  std::cout << '\n';

  return 0;
}