Чтобы обменять (swap) значения двух объектов, нужно присвоить каждому из них значение другого. По умолчанию такой обмен осуществляет стандартный алгоритм swap. Его типичная реализация не расходится с вашими ожиданиями:
namespace std {
template
void swap(T& a, T& b) // меняет местами значения a и b
{
T temp(a);
a = b;
b = temp;
}
}Коль скоро тип поддерживает копирование (с помощью конструктора копирования и оператора присваивания), реализация swap по умолчанию позволяет объектам этого типа обмениваться значениями без всяких дополнительных усилий с вашей стороны.
Стандартная реализация swap, может быть, не приведет вас в восторг. Она включает копирование трех объектов: a в temp, b в a и temp – в b. Для некоторых типов ни одна из этих операция в действительности не является необходимой. Для таких типов swap по умолчанию – быстрый путь на медленную дорожку.
Среди таких типов сразу стоит выделить те, что состоят в основном из указателей на другой тип, содержащий реальные данные. Общее название для таких проектных решений: «идиома pimpl» (pointer to implementation – указатель на реализацию – см. правило 31). Спроектированный так класс Widget может быть объявлен следующим образом:class WidgetImpl { // класс для данных Widget
public: // детали несущественны
…
private:
int a,b,c; // возможно, много данных –
std::vector
…
};
class Widget { // класс, использующий идиому pimpl
public:
Widget(const Widget& rhs);
Widget& operator=(const Widget& rhs) // чтоб скопировать Widget, копируем
{ // его объект WidgetImpl. Детали
… // реализации operator= как такового
*pimpl = *(rhs.pimpl); // см. в правилах 10, 11 и 12
…
}
…
private:
WidgetImpl *pimpl; // указатель на объект с данными
}; // этого WidgetЧтобы обменять значения двух объектов Widget, нужно лишь обменять значениями их указатели pimpl, но алгоритм swap по умолчанию об этом знать не может. Вместо этого он не только трижды выполнит операцию копирования Widget, но еще и три раза скопирует Widgetlmpl. Очень неэффективно! А нам бы хотелось сообщить функции std::swap, что при обмене объектов Widget нужно обменять значения хранящихся в них указателей pimpl. И такой способ существует: специализировать std::swap для класса Widget. Ниже приведена основная идея, хотя в таком виде код не скомпилируется:
namespace std {
template <> // это специализированная версия
void swap
Widget& b) // Widget;
{
swap(a.pimpl, b.pimpl); // для обмена двух Widget просто
} // обмениваем их указатели pimpl
}