...one of the most highly
regarded and expertly designed C++ library projects in the
world.
— Herb Sutter and Andrei
Alexandrescu, C++
Coding Standards
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) { 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 an 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 an rvalue clone_ptr
,
or if that code explicitly gives permission to consider the source of the
copy an 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:
BOOST_COPYABLE_AND_MOVABLE(classname)
BOOST_COPY_ASSIGN_REF(classname)
BOOST_RV_REF(classname)
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.