c++performanceswapapi-designmove-semantics

Should a container type define its own swap function even if its move-semantics has been correctly implemented?


Below is the most common implementation of std::swap:

template<typename T>
void std::swap(T& a, T& b) {
    auto tmp = std::move(a);
    a        = std::move(b);
    b        = std::move(tmp);
}

The C++ standard requires the time complexity of std::swap must be constant. So, if a container type, such as std::vector, std::list, and the like, has correctly implemented its move-ctor & move-assignment-operator; then, it doesn't seem necessary to define a swap member function for the type.

However, every container type in std namespace has defined its own swap member function, or a specialization of std::swap. I think there must be a concrete rationale behind the design, what's that?


Solution

  • The rationale for the std::containers specialized swap is history:

    In C++98/03, there was no move semantics. Copy only. But there was std::swap(x, y), which worked by copying. The std::containers could have used std::swap(x, y) but it would have been very expensive: 1 copy construction and 2 copy assignments.

    By implementing specialization, std::containers could just swap pointers and get an order of magnitude optimization, even without move semantics.

    When C++11 came along (with move semantics), it didn't make sense to remove the specializations. Something would have broken somewhere. It was easier and safer just to upgrade the general swap and and leave the specializations alone.

    Bonus points: It turns out that the specializations are slightly faster than the general swap, but not greatly so. Maybe by a load/store or two. But the main reason is history.