C++ Move Constructors – Motivation and Usage Explained

cprogramming practices

I recently have been reading about move constructors in C++ (see e.g. here) and I am trying to understand how they work and when
I should use them.

As far as I understand, a move constructor is used to alleviate the performance problems caused by copying large objects. The wikipedia page says: "A chronic performance problem with C++03 is the costly and unnecessary deep copies that can happen implicitly when objects are passed by value."

I normally address such situations

  • by passing the objects by reference, or
  • by using smart pointers (e.g. boost::shared_ptr) to pass around the object (the smart pointers get copied instead of the object).

What are the situations in which the above two techniques are not sufficient
and using a move constructor is more convenient?

Best Answer

Move semantics introduce a whole dimension to C++ - it isn't just there to let you return values cheaply.

For example, without move-semantics std::unique_ptr doesn't work - look at std::auto_ptr, which was deprecated with the introduction of move-semantics and removed in C++17. Moving a resource is vastly different from copying it. It allows transfer of ownership of a unique item.

For example, let's not look at std::unique_ptr, since it is fairly well discussed. Let's look at, say, a Vertex Buffer Object in OpenGL. A vertex buffer represents memory on the GPU - it needs to be allocated and deallocated using special functions, possibly having tight constraints on how long it can live. It is also important that only one owner use it.

class vertex_buffer_object
{
    vertex_buffer_object(size_t size)
    {
        this->vbo_handle = create_buffer(..., size);
    }

    ~vertex_buffer_object()
    {
        release_buffer(vbo_handle);
    }
};

void create_and_use()
{
    vertex_buffer_object vbo = vertex_buffer_object(SIZE);

    do_init(vbo); //send reference, do not transfer ownership

    renderer.add(std::move(vbo)); //transfer ownership to renderer
}

Now, this could be done with a std::shared_ptr - but this resource is not to be shared. This makes it confusing to use a shared pointer. You could use std::unique_ptr, but that still requires move semantics.

Obviously, I haven't implemented a move constructor, but you get the idea.

The relevant thing here is that some resources aren't copyable. You can pass around pointers instead of moving, but unless you use unique_ptr, there is the issue of ownership. It is worthwhile to be as clear as possible as to what the intent of the code is, so a move-constructor is probably the best approach.