More C++ Idioms/Move Constructor

From Wikibooks, the open-content textbooks collection

Jump to: navigation, search

Contents

[edit]

Move Constructor

[edit] Intent

To transfer the ownership of a resource held by an object to a new object

[edit] Also Known As

  • Colvin-Gibbons trick

[edit] Motivation

Some objects in C++ exhibit so called move semantics. For example, std::auto_ptr. In the code that follows auto_ptr b ceases to be useful after the creation of object a.

std::auto_ptr <int> b (new int (10));
std::auto_ptr <int> a (b);

The copy constructor of auto_ptr modifies its argument, and hence it does not take a const reference as a parameter. It poses no problem in the above code because object b is not a const 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 trouble is that temporaries are const objects by default (See below for an important exception to this rule). Therefore, they can't be used to call the constructor taking a non-const reference. A move constructor can be used in such cases.

[edit] Solution and Sample Code

template <class T>
class MoveResource
{
  private:
    struct proxy
    {
      T * resource_;
    };
    T * resource_;
  public:
    MoveResource (T * r) : resource_(r) { }  
    MoveResource (MoveResource &m) throw () // Move constructor (note non-const parameter)
      : resource_ (m.resource_)
    {
      m.resource_ = 0;
    }
    MoveResource (proxy p) throw () // The proxy move constructor
      : resource_(p.resource_)
    {
    }
    MoveResource & operator = (MoveResource &m) throw ()
    {
      // copy and swap idiom
      MoveResource temp (m);
      temp.swap (*this);
      return *this;
    }
    MoveResource & operator = (proxy p) throw ()
    {
      // copy and swap idiom
      MoveResource temp (p);
      temp.swap (*this);
      return *this;
    }
    void swap (MoveResource &m) throw ()
    {
      std::swap (this->resource_, m.resource_);
    }
    operator proxy () throw () // A helper conversion function. Note that it is non-const
    {
      proxy p;
      p.resource_ = this->resource_;
      this->resource_ = 0;
      return p;
    }
};

The move constructor/assignment idiom plays an important role in the code snippet below.

MoveResource<int> func()
{
  MoveResource<int> m(new int());
  return m;
}
int main()
{
  MoveResource<int> a(func()); // Assuming this call is not return value optimized (RVO'ed).
}

The function func shown above, returns the object by value i.e., a temporary object is returned. Though MoveResource does not have any copy-constructor, the construction of local variable a in main succeeds, while moving the ownership away from the temporary object. This is possible because of a combination of two subtle (but standard) properties of C++.

  • A sequence of conversions via the proxy object is identified automatically by the compiler.
  • The conversion function operator proxy() is non-const. This member conversion operator is used to modify the temporary object! (an important exception)

The compiler seeks a copy-constructor to initialize object a. However, there is no copy-constructor for the MoveResource class. Compiler identifies that a constructor that accepts a proxy object parameter is provided. So it tries to identify a conversion operator that converts an object from MoveResource to proxy. As a matter of fact, such a conversion operator is also provided (operator proxy()). Upon invocation of this conversion function, the local MoveResource object (m) looses its resource ownership. Only the proxy object knows the pointer to T for a very brief period of time. Subsequently, the converting constructor (the one that takes proxy as a parameter) successfully obtains the ownership (object a in main).

Let's look into the details of how temporary objects are created and used. In fact, the above steps are executed (g++ 4.1.2) not once but twice in exactly the same way. First to create a temporary MoveResource object and later to create the final object a in main. The second exceptional rule of C++ comes into play when a is being created from the temporary MoveResource object. The conversion function (operator proxy()) is called on the temporary MoveResource object. However, it is non-const. Commonly held belief is that temporaries are const objects. Well, almost! A non-const member function can be called on a temporary object unlike real const 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 MoveResource object also looses its ownership as described above. A number of temporary proxy 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).

It is also important that these functions be non-throwing to guarantee at least basic exception guarantee. No exceptions should be thrown in the meanwhile, otherwise there will be resource leaks.

The upcoming feature of C++0x language standard, Rvalue references, will eliminate the need of the Move Constructor idiom.

[edit] Known Uses

std::auto_ptr

[edit] Related Idioms

[edit] References

In other languages