Discussion
Team LiB
Previous Section Next Section

Discussion

A swap function typically looks like this, where U is some user-defined type:



class T {// …


public:


 void swap( T& rhs ) {


   member1_.swap( rhs.member1_ );


   std::swap( member2_, rhs.member2_ );


 }


private:


 U member1_;


 int member2_;


};



For primitive types and for standard containers, std::swap will do. Other classes might implement swapping as a member function under various names.

Consider using swap to implement copy assignment in terms of copy construction. The following implementation of operator= provides the strong guarantee (see Item 71), although at the price of creating an extra object, which can be inappropriate if there are more efficient ways to perform error-safe assignment for T objects:



T& T::operator=( const T& other ) {  // good: Variant #1 (traditional)


 T temp( other );


 swap( temp );


 return *this;


}


T& T::operator=( T temp ) {          // good: Variant #2 (see Item 27)


 swap( temp );                     // note: temp passed by value


 return *this;


}



What if U does not implement a no-fail swap function, as is the case with many legacy classes, and you still need T to support a swap function? All is not lost:

  • If U's copy constructor and copy assignment don't fail (as, again, might be the case with legacy classes), std::swap will work fine on U objects.

  • If U's copy constructor might fail, you can store a (smart) pointer to U instead of a direct member. Pointers are easily swappable. They do incur the additional overhead of one extra dynamic storage allocation and one extra access indirection, but if you store all such members in a single Pimpl object you'll incur the overhead only once for all private members. (See Item 43.)

Never use the trick of implementing copy assignment in terms of copy construction by using an explicit destructor followed by placement new, even though this trick still crops up regularly in C++ forums. (See also Item 99.) That is, never write:



T& T::operator=( const T& rhs ) {            // bad: an anti-idiom


 if( this != &rhs ) {


   this->~T();                               // this technique is evil


    new (this) T( rhs );                     // (see [Sutter00] §41)


 }


 return *this;


}



Prefer to provide a nonmember swap function in the same namespace as your type when objects of your type have a way to exchange their values more efficiently than via brute-force assignment, such as if they have their own swap or equivalent function (see Item 57). Additionally, consider specializing std::swap for your own nontemplate types:



namespace std {


 template<> void swap( MyType& lhs, MyType& rhs) {   // for MyType objects,


  lhs.swap( rhs );                                   // use MyType::swap


 }


}



The standard does not allow you to do this when MyType is itself a template class. Fortunately, this specialization is just a nice-to-have; the primary technique is to provide a type-customized swap as a nonmember in the same namespace as the type.

    Team LiB
    Previous Section Next Section