Move constructor — Q&A

After a brief introduction to Move constructor, it is time to get into some technical details of implementing your own move constructor. In this post I try to answer some questions that I saw on the web and that I used to ask myself. Read on for some practical aspects of creating move constructors. Feel free to ask your own questions in the comments.

When should I define move constructor for my class?

It greatly depends on what your class does and how it is implemented. First, for ‘aggregate’ classes, which only group other data for convenience/clarity, move constructors will be implicitly generated by the compiler. Consider the following class.

struct Country {
  std::string name;
  std::vector<std::string>  cities;

In a typical C++ struct many special member functions — like copy constructor, copy assignment, destructor — are auto-generated. This also includes move constructor (and move assignment).

For more sophisticated classes, which encapsulate their implementation details, the answer is more interesting. One of the main goals of move semantics (move constructor, move assignment) is to give the compiler two tools for implementing value semantics (passing arguments by value, returning by value) for user defined types:

  1. Making two identical objects out of one — it needs to be expensive.
  2. Moving one object from one memory location to the other — it can be made very fast.

If for your class it is possible to implement move constructor that would be faster than the copy constructor, you should implement it for run-time speed optimization purposes. We have seen how it can be implemented for vector in the previous post. However, it is not for all types that such move constructor, faster than a copy constructor, can be implemented. Consider the following matrix representation.

class Matrix {
  std::complex<long double> data[1000][1000];

Because all memory required for matrix representation is declared in class scope (unlike in vector, which uses heap-allocated memory) there is no way to apply only a small number of assignments. We will need to do a copying for each array element. There is no point in defining move constructor, as it will be no faster than copying.

Another valid reason for providing your move constructor if you want to enable your type that is non-copyable (because it is RAII-like and represents a resource) to be still passed by value where copying is not required, and stored in STL containers. Such unique ownership semantics are explained in more detail in this post.

Can move constructor copy?

The C++ standard expects the following of your types’ move constructor:

  1. the value of the new object should be the same as the original object had before the move constructor was called,
  2. the original object should be left in a state where it can be correctly destroyed or assigned to without causing resource leak or undefined behavior.

If you just implement a regular copying in your move constructor you do satisfy the two constraints, so it is correct to do so; however, there is really no point in doing so. If you cannot or do not want to offer move constructor that works more efficient than the copy constructor just don’t define it at all. The copy constructor will do in all cases. In cases where compiler can deduce the moving context — that is, when constructing a new object from a temporary — copy constructor is already chosen for C++03, and C++11 is supposed to be backwards compatible. But even in case of programmer’s hints in form of function std::move, the copy constructor without move constructor is sufficient.

This requires some additional explanation. Function std::move is defined in the following way:

template< typename T > 
typename remove_reference<T>::type&&  move(T&& t) noexcept {
  return static_cast<typename remove_reference<T>::type&&>(t);

The interpretation of this declaration is very tricky. Sign && no longer indicates an r-value reference. The only thing it does is to change its argument, which may be either an l-value or an r-value, into an r-value. So when you use it when calling a constructor:

std::vector<int> vecA = { 1, 2, 4, 8 };
auto vecB( std::move(vecA) );

The r-value reference in move constructor is preferred over const l-value reference in copy constructor — these are the rules for binding expression values to references. If move constructor is not available, the second preference — copy constructor — is chosen. So, the real meaning of std::move is “use move constructor if available, otherwise use copy constructor.”

One other thing to be observed, when it comes to answering the original question, is that for built-in types, like pointers or integers, move constructor is defined to do the copying.

Can move constructor throw?

Another advantage of move constructor is that unlike for copying it may be easy to implement it in the way that no exceptions are thrown from the constructor. Again, it can be illustrated with the vector constructor example from the previous post. While copy constructor requires allocating memory for the copies of vector elements and copying itself (either operation can throw exceptions), move constructor only executes six pointer assignments. It is beneficial, when implementing a no-throw move constructor, to annotate it with noexcept:

template< typename T >
class vector {
    T * begin_;
    T * end_;
    T * capacityEnd_;

    vector( vector && tmp ) noexcept; // no-throw move
    // ...

The noexcept specification can be detected at compilation time and the compiler may choose more effective algorithm implementations based on it. One of the tools for doing this is an another standard library forwarding function std::move_if_noexcept. It chooses the move constructor only if it is declared as non-throwing or if copy constructor is not available; otherwise it chooses a copy constructor.

Nonetheless, it is not a must that move constructor should not throw exceptions and recently the ISO C++ Committee decided to allow the move constructor of class template std::future to throw exceptions. You can read more about it in N3269.

Can implicitly defined move constructor spoil my program?

It is possible to define a class that would work correctly (guarantee its invariant) in C++03 but whose invariants would be broken in C++11 when move constructor is implicitly generated. Dave Abrahams provided interesting examples in his article. In C++11 it was decided that the compiler will implicitly generate move constructor as member-wise moves, unless you have explicitly defined a copy constructor or copy/move assignment or a destructor. The goal was to achieve a balance between breaking some of existing code and providing useful optimizations based on move constructors.

Note that rules for implicitly generating move constructor in C++11 are still safer than rules for implicitly generating copy constructor in C++03. Also note that in C++03 implicit generation of copy constructor for classes with explicitly defined destructor is deprecated.

This entry was posted in programming and tagged , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )


Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.