TGViewer
Грокаем C++ Грокаем C++ @grokaemcpp · 9.35K subscribers
Post #399 4.72K
​​Правильный swap двух объектов Ч1

В статье про swap идиому мы реализовали свап объектов класса через дружественную функцию. Сегодня поговорим почему так делать правильно.

В принципе нам нужна функциональность, которая сможет обменять данных двух объектов местами. Она может называться как угодно, никто не запрещает назвать вам swap функцию как TheMostTrickyFunction.

Но так делать не очень удобно. Все привыкли использовать std::swap для обмена значений. Поэтому логично как минимум назвать функцию swap.

Дальше будем рассматривать по очереди возможные варианты.

Можно определить метод swap внутри класса:

struct my_type
{
void swap(my_type&) { /* swap members / }
};


И хоть это будет работать в пользовательском коде just fine, но мы не сможем для такого типа например использовать std::sort, которая вызывает свободную функцию swap.

Но мы можем найти примеры такого дизайна даже в стандартной библиотеке. Например std::vector имеем метод swap, который обменивает данные двух векторов. Но тут важен контекст: до появления мув-семантики обмен векторов через std::swap приводило бы к нежелательным копированиям. Поэтому в те времена многие объекты имели свою оптимизированную версию в виде метода.

Сейчас вы можете использовать std::swap на двух векторах и не парится по поводу перфоманса. Так что просто swap метод класса нам не подходит.

Раз мы хотим использовать std::swap, то может тогда просто специализируем эту функцию для нашего типа в скоупе std? Давайте попробуем.

namespace std
{
template <>
void swap(my_type& one, my_type& two)
{
one.swap(two);
}
}


И это даже может и заработает. Но С++20 говорит нам, что специализировать шаблонные функции из неймспейса std - неопределенное поведение. Поэтому этот вариант - совсем не вариант.

Попробуем определение свободной функции swap в неймспейсе класса

namespace my_ns {

struct my_type
{
void swap(my_type&) { / swap members */ }
};

void swap( my_type<T> & lhs, my_type<T> & rhs ) noexcept
{
lhs.swap(rhs);
}

}


Однако это выглядит просто как обертка для метода swap, который больше нигде не используется. Может как-то схлопнуть две эти сущности?

Сделаем эту свободную функцию дружественной нашему классу! Тогда можно выкинуть ненужный метод и оставить просто функцию.

struct my_type
{
friend void swap(my_type& first, my_type& second) noexcept {
// swap
}
};


Так-то лучше. Все работает и выглядит культурно. Далее поговорим про то, что должно быть внутри функции.

Be nice. Stay cool.

#template #cppcore #cpp20
  • ❤‍🔥 15
  • 👍 7
  • ⚡ 2
  • ❤ 2
More from @grokaemcpp
  1. Oct 14, 2026​​Что внутри std::condition_variable? #опытным Более менее все знакомы с примитивом синхро…
  2. Oct 12, 2026​​WAT #новичкам Как вы думаете, результат выражения: bool math = -1 < 1U; будет правдой ил…
  3. Oct 8, 2026​​Strict weak ordering #опытным На первый взгляд, всё выглядит рабочим: мы создаём 40 зака…
  4. Oct 7, 2026​​Где-то баг... #опытным Вот вам код: struct Order { int price; int id; }; int main() { st…
  5. Oct 5, 2026Откуда spurious wakeup на кондваре? #опытным У кондваров есть метод std::condition_variabl…
  6. Oct 1, 2026​​Stacktrace. Tips #опытным Чтобы полноценно работать со стандартными трейсами, нужно знат…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →