Daily bit(e) C++. Корректный вызов std::swap

Добавлено 7 августа 2026 в 08:19

Daily bit(e) C++ 62, корректный вызов std::swap для пользовательских типов во избежание проблем с ADL (argument-dependent lookup).

Daily bit(e) C++

При использовании std::swap с пользовательскими типами необходимо быть осторожным с последствиями поиска, зависимого от аргументов (ADL).

Квалифицированный вызов std::swap всегда вызовет реализацию по умолчанию. Неквалифицированный вызов может с помощью ADL найти пользовательскую реализацию, но не увидит реализацию по умолчанию.

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

В качестве альтернативы, в C++20 квалифицированный вызов std::ranges::swap всегда будет выполнять правильные действия.

#include <algorithm>

namespace MyNamespace {
struct MyClass 
{
    // Использовать встроенную дружескую функцию, чтобы
    // реализовать пользовательский swap.
    friend void swap(MyClass&, MyClass&) {}
};

struct MyOtherClass {};
}

// Обратите внимание, что объявление с именем swap,
// не являющееся функцией, в глобальной области видимости
// будет обнаружено при неквалифицированном поиске и
// предотвратит ADL.
// constexpr auto swap = "Hello!";

MyNamespace::MyClass a, b;
MyNamespace::MyOtherClass x, y;

// Полностью квалифицированный вызов всегда вызывает std::swap
std::swap(a,b); // вызывает std::swap
std::swap(x,y); // вызывает std::swap

// Нет подходящего swap для MyOtherClass.
swap(a,b); // вызывает MyNamespace::swap
// swap(x,y); // не скомпилируется

// Вытягиваем std::swap в качестве дефолтного в локальную
// область видимости:
{
using std::swap;
swap(a,b); // вызывает MyNamespace::swap
swap(x,y); // вызывает std::swap
}

// std::ranges::swap из C++20 делает это правильно:
std::ranges::swap(a,b); // вызывает MyNamespace::swap
std::ranges::swap(x,y); // дефолтный swap

Пример на Compiler Explorer

Теги

C++ / CppDaily bit(e) C++Программирование