Tag Archives: Move Semantics

std::move()

The std::move() operator is a cast that gets an xvalue reference to an object, allowing it to be moved. Moving the object instead of copying it increases efficiency.

For example, consider the following swap() function:

template <typename T>
void swap(T& a, T& b)
{
    T tmp(a);
    a = b;
    b = tmp;
}

When this runs, a is first copied to tmp, then b is copied to a, and finally tmp is copied to b. If a and b were expensive objects to copy, this would be very inefficient.

With move(), we can rewrite the swap() function this way:

#include <utility>
template <typename T>
void swap(T& a, T& b)
{
    T tmp(std::move(a));
    a = std::move(b);   
    b = std::move(tmp);
}

Now if a and b are of a class that has a move constructor and assignment operator, they will be efficiently moved rather than copied.

std::vector::emplace_back()

The emplace_back() method is a C++11 addition to vector that makes it more efficient to add an object to the back of a vector.

Consider the following code:

#include <iostream>
#include <vector>
#include <string>

class X
{
public:
    X(std::string s, int i)
        : s_(s), i_(i)
    {
        std::cout << "Constructor called for " << s_ << i_ << "\n";
    }
    X(const X& other)
        : s_(other.s_), i_(other.i_)
    {
        std::cout << "Copy constructor called for " << s_ << i_ << "\n";
    }
private:
    std::string s_;
    int i_;
};

int main()
{
    std::vector<X> v;
    std::cout << "push_back:" << "\n";
    v.push_back(X("A", 1));
}

An object of a class with 2 member variables initialised in the constructor is first constructed, and added to the end of a vector with push_back().

This is a 2-step process:

  1. Construct the object
  2. Copy it into the vector’s storage

The output from the program shows this process:

push_back:
Constructor called for A1
Copy constructor called for A1

Constructing an object, copying it, and then destroying it when it goes out of scope is not very efficient, and it’s this inefficiency that emplace_back is designed to remove.

emplace_back takes a variable list of arguments, which must match those of one of the object’s constructors, and these are forwarded to that constructor so that the object is constructed in-place.

    std::cout << "emplace_back:" << "\n";
    v.emplace_back("A", 1);

This produces the following output:

emplace_back:
Constructor called for A1

So the unnecessary copy has not happened.

Related

Move semantics in C++

Consider the following simple string class:

class String
{
public:
    String()
        : data_(strdup_("")), len_(0)
    {
    }

    String(const char* str)
        : data_(strdup_(str)), len_(std::strlen(data_))
    {
    }

    String(const char *str, size_t len)
        : data_(strndup_(str, len)), len_(len)
    {
    }

    String(const String& other)
        : data_(strdup_(other.data_)), len_(other.len_)
    {
    }

    ~String()
    {
        delete[] data_;
    }

    String& operator=(String other)
    {
        std::swap(data_, other.data_);
        return *this;
    }

    String& operator+=(const String& other)
    {
        size_t len = len_ + other.len_;
        char *data = new char[len + 1];
        std::strcpy(data, data_);
        std::strcat(data, other.data_);
        delete[] data_;
        data_ = data;
        len_ = len;
        return *this;
    }

    String substring(size_t start, size_t len)
    {
        if (start < len_ - 1 && len <= len_ - start) {
            return String(&data_[start], len);
        }
        return String();
    }

    friend String operator+(String lhs, const String& rhs);
    friend std::ostream& operator<<(std::ostream& os, const String& str);

private:
    char* data_;
    size_t len_;
};

The important things to note are:

  • It manages a dynamically allocated block of memory, so it needs to observe the Rule of Three
  • In addition to its constructors, it has two functions that return an object: operator+(), and substring()

Now consider the following uses of the class:

int main()
{
    String a = "The quick brown fox jumps over";
    String b = " the lazy dog";

    String x = a;                  // Copy an existing object
    String y = a + b;              // Create a new object by addition and then copy it
    String z = a.substring(16, 3); // Create a new object by function call and then copy it

    std::cout << x << "\n";
    std::cout << y << "\n";
    std::cout << z << "\n";
}

There are 3 copying operations in this code. In the first, we are copying the instance a, so we naturally expect to be able to use a again after the copy has been made.
In the other two though, temporary objects are created (by operator+() and substring respectively), and there is no way we could use them before they are destroyed at the end of the expression. These temporary objects are called rvalues because they only occur on the right-hand side of an expression, and can never be on the left-hand side of one.

As these objects can never be used, it’s wasteful and unnecessary for them to go through the same construction and destruction process as objects we do use. In particular, it shouldn’t be necessary to go through the process of copying the data_ member in the copy constructor, only for it to be deleted immediately after the copy is made.

What move semantics provide, amongst other things, is a way of writing a copy constructor that behaves differently when the object being constructed is an rvalue, called a move constructor. Below is a move constructor for our string class:

String(String&& other)
{
    data_ = other.data_;
    other.data_ = nullptr;
}

Note the &&, which indicates an rvalue reference variable. It is this that makes the constructor a move constructor. This constructor will be called in the situations like those above where the compiler can tell that the object being copied is an rvalue. In this case we can simply steal the data_ member from the soon-to-be-destroyed other object by making it the data_ member of the new object, and then setting it the other object’s member to nullptr so that the block won’t be deleted when the other object goes out of scope. This makes the whole process of copy initialisation much more efficient when the object being copied is an rvalue.

There is a lot more to rvalues and move semantics than this, but as a first step it is well worth considering adding a move constructor to a class that has methods that return an object, as these objects are quite likely to be constructed as rvalues in practice.