Move semantics concerns avoiding unnecessary copies whenever possible.

The compiler knows that temporary objects (for example, vector.push_back(getData())) can be copy-elided. In unapparent cases, we use std::move() to tell the compiler that the object is “no longer needed” and “its content can be stolen/moved” (i.e. the ownership is moved).

Sometimes it makes sense to reuse object after moved:

std::vector<std::string> rows;
std::string row;
while(std::getline(std::cin, row)) {
  rows.push_back(std::move(row));
}

Move semantics with OOP

With rvalue references (double &), programmers can define move logic for objects.

Rules of 5: all or nothing: destructor, copy constructor, copy assignment constructor, move constructor, move assignment constructor

Overloading

// only “objects with no name” (i.e. temp objects) or std::move()
void foo(T&& obj)
// compiles but useless because obj is not needed but we cannot
// modify or steal data!
void foo(const T&& obj)

Default support

If no move semantics is provided, copy semantics is used. But we can disable this copy fallback behavior (this is used by move-only objects, e.g. std::thread, std::unique_ptr).

By default, move semantics is defined for objects. However it is disabled if any special member function (destructor, copy constructor, assignment operator) exists.

For polymorphism hierarchy, a declared virtual destructor also disables move semantics (for the members of the objects…)

Perfect forwarding

The act to forward arguments while preserving their value categories.

Note that:

void foo(const T&& obj) {
  // inside here obj is no longer interpreted as rvalue reference (move semantics lost)
  // there calling vector.push_back(obj); would be copying
  // if we want to move, need vector.push_back(std::move(obj));
}

Perfect forwarding of parameters needs:

  1. Template parameter