...one of the most highly
regarded and expertly designed C++ library projects in the
world.
— Herb Sutter and Andrei
Alexandrescu, C++
Coding Standards
For classes made up of other classes (via either composition or inheritance),
the move constructor and move assignment can be easily coded using the boost::move
function:
class Base { BOOST_COPYABLE_AND_MOVABLE(Base) public: Base(){} Base(const Base &x) {/**/} // Copy ctor Base(BOOST_RV_REF(Base) x) {/**/} // Move ctor Base& operator=(BOOST_RV_REF(Base) x) {/**/ return *this;} // Move assign Base& operator=(BOOST_COPY_ASSIGN_REF(Base) x) {/**/ return *this;} // Copy assign virtual Base *clone() const { return new Base(*this); } virtual ~Base(){} }; class Member { BOOST_COPYABLE_AND_MOVABLE(Member) public: Member(){} // Compiler-generated copy constructor... Member(BOOST_RV_REF(Member)) {/**/} // Move ctor Member &operator=(BOOST_RV_REF(Member)) // Move assign {/**/ return *this; } Member &operator=(BOOST_COPY_ASSIGN_REF(Member)) // Copy assign {/**/ return *this; } }; class Derived : public Base { BOOST_COPYABLE_AND_MOVABLE(Derived) Member mem_; public: Derived(){} // Compiler-generated copy constructor... Derived(BOOST_RV_REF(Derived) x) // Move ctor : Base(boost::move(static_cast<Base&>(x))), mem_(boost::move(x.mem_)) { } Derived& operator=(BOOST_RV_REF(Derived) x) // Move assign { Base::operator=(boost::move(static_cast<Base&>(x))); mem_ = boost::move(x.mem_); return *this; } Derived& operator=(BOOST_COPY_ASSIGN_REF(Derived) x) // Copy assign { Base::operator=(static_cast<const Base&>(x)); mem_ = x.mem_; return *this; } // ... };
Important | |
---|---|
Due to limitations in the emulation code, a cast to |
Each subobject will now be treated individually, calling move to bind to the
subobject's move constructors and move assignment operators. Member
has move operations coded (just like
our earlier clone_ptr
example)
which will completely avoid the tremendously more expensive copy operations:
Derived d; Derived d2(boost::move(d)); d2 = boost::move(d);
Note above that the argument x is treated as a lvalue reference. That's why
it is necessary to say move(x)
instead of just x when passing down to the base class. This is a key safety
feature of move semantics designed to prevent accidently moving twice from
some named variable. All moves from lvalues occur explicitly.