C++ Programming Tips: A Better Swap Function

C++ provides us with a default swap function in the std namespace:

namespace std{    template<typename T>    void swap(T& a,T& b){        T temp(a);        a = b;        b = temp;    }}

As we can see, as long as we write a proper copy constructor and assignment operator for the class, this default swap can effectively swap two objects.However, sometimes certain technical methods can provide better and faster swapping techniques, making the default version of swap too slow. Therefore, we need to know how to customize a better swap.1. Pimpl TechniqueThe pimpl technique is a primary reason why the default swap is not suitable. If we create a class using this method, it would look like this:

class StudentImpl{public:    ...private:    int ID;    // There are many data here    std::string name;    ...};// Pimpl technique implementationclass Student{public:    Student(const Student& stu);    Student& operator=(const Student& stu){        *p_impl = *(stu.p_impl);    }private:    StudentImpl* p_impl;};

As we can see, pimpl places the data of the Student class in another StudentImpl class, and the Student class only retains a pointer to StudentImpl. Thus, during swapping, we only need to swap the StudentImpl pointer.At this point, there is a conflict with the default swap function, and we need a swap function that only swaps pointers, as this is faster.2. Fully Specialized Swap FunctionA straightforward approach is to fully specialize a swap function for Student, but since p_impl is private, the swap function cannot access it directly. We can mimic the STL’s implementation method:

class Student{public:    void swap (Student& stu){        using std::swap;  // Why this line? See subtitle four        swap(p_impl,stu.p_impl);  // Swap the two pointers    }};// Fully specialized swapnamespace std{    template<>    void swap<Student>(Student& a,Student& b){        a.swap(b);    }}

First, we implement a public swap function in the Student class to swap pointers. Then, we fully specialize the std swap to call this public swap function. This ensures consistency with the STL.3. What About Template Classes?If Student and StudentImpl are template classes, full specialization cannot be used, and partial specialization cannot be applied to a function like swap.The common practice is to overload a swap, but if we do that, it generally results in an error. (Although some compilers may not, that is non-standard.) This is because std cannot have new elements added.Therefore, we will overload swap in the namespace where the class resides:

namespace StudentNs{    // Template class    template<typename T>    class Student{ ... };    // Overload swap    template<typename T>    void swap(Student<T>& a,Student<T>& b){        a.swap(b);    }}

4. The Version of Swap CallIn the above, we left a question: why do we need to write using std::swap;? This is to ensure that swap calls the correct version.The C++ lookup for the version of swap follows the order of global/class namespace swap, then std namespace swap (first fully specialized, then default). The line using std::swap; exposes std within the function; otherwise, C++ will not look for the swap in the std namespace.Therefore, when using swap, it should be written like this:

// Expose stdusing std::swap;// Do not add std::swap(a,b);

Leave a Comment