More C++ Idioms/Move Constructor

= Move Constructor =

Intent
To transfer the ownership of a resource held by an object to another object in C++03. Note: In C++11, move-constructors are implemented using the built-in rvalue reference feature.

Also Known As

 * Colvin-Gibbons trick
 * Source and Sink Idiom

Motivation
Some objects in C++ exhibit so-called move semantics. For example,. In the code that follows,  ceases to be useful after the creation of object. The copy constructor of  modifies its argument, and hence it does not take a   reference as a parameter. It poses no problem in the above code because object  is not a   object. But it creates a problem when a temporary is involved.

When a function returns an object by value and that returned object is used as an argument to a function (for example to construct another object of the same class), compilers create a temporary of the returned object. These temporaries are short lived and as soon as the statement is executed, the destructor of the temporary is called. The temporary therefore owns its resources for a very short time. The problem is that temporaries are quite like  objects (it makes little sense to modify a temporary object). Therefore, they can not bind to a non-const reference and as a result, can not be used to call the constructor taking a non- reference. A move constructor can be used in such cases.

Solution and Sample Code
The move constructor/assignment idiom plays an important role in the code snippet shown next. The function  returns the object by value i.e., a temporary object is returned. Though  does not have any copy-constructor, the construction of local variable   in   succeeds, while moving the ownership away from the temporary object.

The idiom uses a combination of three interesting and standard features of C++.


 * A copy-constructor need not take its parameter by  reference. The C++ standard provides a definition of a copy-constructor in section 12.8.2. Quoting -- A non-template constructor for class X is a copy constructor if its first parameter is of type ,  ,   or  , and either there are no other parameters or else all other parameters have default arguments. -- End quote.
 * A sequence of conversions via the  object is identified automatically by the compiler.
 * Non-const functions can be called on temporary objects. For instance, the conversion function  is non-const. This member conversion operator is used to modify the temporary object.

The compiler seeks a copy-constructor to initialize object. There is a copy-constructor defined but it accepts its parameter by non-const reference. The non-const reference does not bind to the temporary object return by function. Therefore, the compiler looks for other options. It identifies that a constructor that accepts a  object is provided. So it tries to identify a conversion operator that converts the  object to. As a matter of fact, such a conversion operator is also provided. Note also that the conversion operator is non-const. That's not a problem because C++ allows invoking non-const functions on temporaries. Upon invocation of this conversion function, the local  object (m) loses its resource ownership. Only the  object knows the pointer to   for a very brief period of time. Subsequently, the converting constructor (the one that takes  as a parameter) successfully obtains the ownership (object   in  ).

Let's look into the details of how temporary objects are created and used. In fact, the above steps are executed not once but twice in exactly the same way. First to create a temporary  object and later to create the final object   in. The second exceptional rule of C++ comes into play when  is being created from the temporary   object. The conversion function is called on the temporary   object. Note that it is non-const. A non-const member function can be called on a temporary object unlike real  objects. Section 3.10.10 in C++ ISO/IEC 14882:1998 standard clearly mentions this exception. More information on this exception can be found here. The conversion operator happens to be a non-const member function. Therefore, the temporary  object also loses its ownership as described above. Several temporary  objects also are created and destroyed when the compiler figures out the right sequence of conversion functions. It is possible that the compiler might eliminate certain temporaries using return value optimization (RVO). To observe all the temporaries, use --no-elide-constructors option to the gcc compiler.

This implementation of the move constructor idiom is useful to transfer the ownership of resources (e.g., heap-allocated memory) in and out of a function. A function that accepts  by-value serves as a sink function because the ownership is transferred to a function deeper inside the call-stack whereas a function that returns   by-value serves as source because the ownership is moved to an outer scope. Hence the name source-and-sink idiom.

Safe Move Constructor

Although the source-and-sink idiom is quite useful at the boundaries of a function call, there are some undesirable side-effects of the above implementation. Particularly, simple expressions that look like copy-assignment and copy-initialization do not make copies at all. For instance, consider the following code:

object  looks like it has been copy-assigned from. However, it silently steals the underlying resource from  leaving it behind as if it is default initialized. The  object no longer owns the heap allocated integer. Such behavior is quite surprising to most programmers. Moreover, generic code written assuming regular copy semantics will likely have very undesirable consequences.

The semantics mentioned above are commonly known as Move Semantics. It is a powerful optimization. However, part of the problem of this optimization, implemented in the above form, is the lack of error messages during compilation of assignment and copy-initialization of expressions that look like a copy but in fact perform a move. Specifically, implicitly copying one  from another named   (e.g.,  ) is unsafe and therefore should be indicated by an error.

Note that stealing resources from a temporary object is perfectly acceptable because they do not last long. The optimization is meant to be used on rvalues (temporary objects). Safe move constructor idiom is a way of achieving this distinction without language-level support (rvalue references in C++11).

The safe move constructor idiom prevents implicit move semantics from named objects by declaring private constructor and assignment operator functions.

Key thing to note here is that the parameter types are non-const. They bind to named objects only and not to temporary objects. This little change alone is sufficient to disable implicit moves from named objects but allow moves from temporary objects. The idiom also provides a way to do explicit move from a named object, if desired.

The  function above is also quite idiomatic. It must accept the parameter as a non-const reference to the object so that it binds to l-values only. It turns a non-const  lvalue (named object) into an rvalue (temporary object) via conversion to. Two explicit conversions are necessary inside the  function to avoid certain language quirks. Ideally, the  function should be defined in the same namespace as that of   class so that the function can be looked up using argument dependent lookup (ADL).

Function  returns a local   object. Because it is a named object,  must be used to turn it into an rvalue before returning. In, first   is initialized directly from the temporary returned by. Simple assignment to another  fails with a compiler error but explicit move works. Similarly, implicit copy to the  function does not work. Programmer must explicitly express its interest in moving the object to the  function.

Finally, it is also important that the key functions (construction from and to ) be non-throwing to guarantee at least basic exception guarantee. No exceptions should be thrown in the meanwhile, otherwise there will be resource leaks.

C++11 provides rvalue references, which support language-level move semantics, eliminating the need for the Move Constructor idiom.

Historical Alternatives

An old (but inferior) alternative is to use a  member to keep track of ownership to sneak past the const-correctness checks of the compiler. The following code shows how the  may be implemented alternatively.

This technique, however, has several disadvantages.


 * The copy constructor and copy-assignment operators do not make logical copies. On the contrary, they transfer the ownership from right hand side object to . This fact is not reflected in the interface of the second implementation of  . The first   class accepts a   object by non-const reference, which prevents the use of the object in contexts where a copy is expected.
 * The Const auto_ptr idiom, which is C++03's way of preventing transfer of ownership is simply not possible with the second implementation of the  class. This idiom depends on the fact that   auto_ptr objects cannot bind to the non-const reference parameters of the move-constructor and move-assignment operators. Making the parameters   references defeats the purpose.
 * The  flag 'owner' increases the size of the structure. The increase in size is substantial (essentially double) for classes like , which otherwise contain just a pointer. Doubling of the size is due to compiler-enforced alignment of data.

Known Uses

 * Boost.Move library
 * std::auto_ptr uses "unsafe" version of move constructor.
 * The unique_ptr C++03 emulation by Howard Hinnant uses the safe move constructor idiom.

Related Idioms

 * Resource Acquisition Is Initialization (RAII)
 * Scope Guard
 * Resource Return
 * Const auto_ptr