Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

This is the documentation for an old version of Boost. Click here to view this page for the latest version.
PrevUpHomeNext

Implementing copyable and movable classes

Copyable and movable classes in C++0x
Copyable and movable classes in portable syntax for both C++03 and C++0x compilers

Consider a simple handle class that owns a resource and also provides copy semantics (copy constructor and assignment). For example a clone_ptr might own a pointer, and call clone() on it for copying purposes:

template <class T>
class clone_ptr
{
   private:
   T* ptr;

   public:
   // construction
   explicit clone_ptr(T* p = 0) : ptr(p) {}

   // destruction
   ~clone_ptr() { delete ptr; }

   // copy semantics
   clone_ptr(const clone_ptr& p)
      : ptr(p.ptr ? p.ptr->clone() : 0) {}

   clone_ptr& operator=(const clone_ptr& p)
   {
      if (this != &p)
      {
         T *p = p.ptr ? p.ptr->clone() : 0;
         delete ptr;
         ptr = p;
      }
      return *this;
   }

   // move semantics
   clone_ptr(clone_ptr&& p)
      : ptr(p.ptr) { p.ptr = 0; }

   clone_ptr& operator=(clone_ptr&& p)
   {
      if(this != &p)
      {
         std::swap(ptr, p.ptr);
         delete p.ptr;
         p.ptr = 0;
      }
      return *this;
   }

   // Other operations...
};

clone_ptr has expected copy constructor and assignment semantics, duplicating resources when copying. Note that copy constructing or assigning a clone_ptr is a relatively expensive operation:

clone_ptr<Base> p1(new Derived());
// ...
clone_ptr<Base> p2 = p1;  // p2 and p1 each own their own pointer

clone_ptr is code that you might find in today's books on C++, except for the part marked as move semantics. That part is implemented in terms of C++0x rvalue references. You can find some good introduction and tutorials on rvalue references in these papers:

When the source of the copy is known to be a rvalue (e.g.: a temporary object), one can avoid the potentially expensive clone() operation by pilfering source's pointer (no one will notice!). The move constructor above does exactly that, leaving the rvalue in a default constructed state. The move assignment operator simply does the same freeing old resources.

Now when code tries to copy a rvalue clone_ptr, or if that code explicitly gives permission to consider the source of the copy a rvalue (using boost::move), the operation will execute much faster.

clone_ptr<Base> p1(new Derived());
// ...
clone_ptr<Base> p2 = boost::move(p1);  // p2 now owns the pointer instead of p1
p2 = clone_ptr<Base>(new Derived());   // temporary is moved to p2
}

Many aspects of move semantics can be emulated for compilers not supporting rvalue references and Boost.Move offers tools for that purpose. With Boost.Move we can write clone_ptr so that it will work both in compilers with rvalue references and those who conform to C++03. You just need to follow these simple steps:

Let's see how are applied to clone_ptr:

template <class T>
class clone_ptr
{
   private:
   // Mark this class copyable and movable
   BOOST_COPYABLE_AND_MOVABLE(clone_ptr)
   T* ptr;

   public:
   // Construction
   explicit clone_ptr(T* p = 0) : ptr(p) {}

   // Destruction
   ~clone_ptr() { delete ptr; }

   clone_ptr(const clone_ptr& p) // Copy constructor (as usual)
      : ptr(p.ptr ? p.ptr->clone() : 0) {}

   clone_ptr& operator=(BOOST_COPY_ASSIGN_REF(clone_ptr) p) // Copy assignment
   {
      if (this != &p){
         T *tmp_p = p.ptr ? p.ptr->clone() : 0;
         delete ptr;
         ptr = tmp_p;
      }
      return *this;
   }

   //Move semantics...
   clone_ptr(BOOST_RV_REF(clone_ptr) p)            //Move constructor
      : ptr(p.ptr) { p.ptr = 0; }

   clone_ptr& operator=(BOOST_RV_REF(clone_ptr) p) //Move assignment
   {
      if (this != &p){
         delete ptr;
         ptr = p.ptr;
         p.ptr = 0;
      }
      return *this;
   }
};

Question: What about types that don't own resources? (E.g. std::complex?)

No work needs to be done in that case. The copy constructor is already optimal.


PrevUpHomeNext