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

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

Очень хочется написать просто типа такого:

struct my_type
{
friend void swap(my_type& first, my_type& second) noexcept {
std::swap(first.member_1, second.member_1);
std::swap(first.member_2, second.member_2);
}
Type1 member1;
Type2 member2;
};


И это работает для тривиальных типов Type1, Type2. Если у вас тривиальные члены - пожалуйста пользуйтесь.

Но если Type1, Type2 - пользовательские типы не из стандартной библиотеки? Понятное дело, что для стдшных типов есть специализации std::swap. Но вот для обычных смертных - нет. И если для этого смертного типа определена своя функция swap, которая лучше знает, как обменивать данные, то таким образом вы никогда ее не вызовете. А если какой-то из ваших мемберов не удовлетворяет условию std::is_move_constructible_v<T> && std::is_move_assignable_v<T>, то вы вообще не скомпилируете этот код.

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

Такая штука есть и называется она ADL или Argument Dependent Lookup. С ее помощью мы можем найти нужную функцию по типу аргумента. Можно написать:

struct my_type
{
friend void swap(my_type& first, my_type& second) noexcept {
swap(first.member_1, second.member_1);
swap(first.member_2, second.member_2);
}
Type1 member1;
Type2 member2;
};


И если для Type1 и Type2 определены свои функции swap, то они найдутся и все скомпилируется.

Однако выискивать для каждого типа своего мембера, есть ли у него своя собственная swap - задача неблагодарная. Хотелось бы и рыбку съесть и машку за ляшку не задумываться о выборе нужной функции и положить эту задачу на плечи компилятора. И это можно сделать!

struct my_type
{
friend void swap(my_type& first, my_type& second) noexcept {
using std::swap;
swap(first.member_1, second.member_1);
swap(first.member_2, second.member_2);
}
Type1 member1;
Type2 member2;
};


Какая тут магия произошла. Мы попрежнему используем ADL для поиска наиболее подходящей функции. Но если мы ее не нашли, то остается бэкап в виде std::swap, которая может вызваться благодаря using std::swap.

Примерно так и выглядит "каноничный" вид функции swap для ваших кастомных классов.

Sit on both chairs. Stay cool.

#cppcore
  • 🔥 22
  • 👍 15
  • ❤ 4
  • 🐳 1
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 →