- является правильное использование в
swap
. Пишите так, если вы пишете "библиотечный" код и хотите включить ADL (поиск, зависимый от аргументов) swap
. Кроме того, это не имеет ничего общего с SFINAE.
template<class T>
void foo(T& lhs, T& rhs) {
using std::swap;
swap(lhs, rhs);
}
- Это правильный способ предоставить
swap
функцию вашему классу.
namespace Foo {
class Bar{};
void swap(Bar& lhs, Bar& rhs) {
}
}
Если swap
теперь используется, как показано в 1), ваша функция будет найдена. Кроме того, вы можете сделать эту функцию другом, если вам это абсолютно необходимо, или предоставить член, swap
который вызывается бесплатной функцией:
class Bar{
public:
friend void swap(Bar& lhs, Bar& rhs) {
}
};
class Bar{
public:
void swap(Bar& other) {
}
};
void swap(Bar& lhs, Bar& rhs) {
lhs.swap(rhs);
}
...
- Вы имеете в виду явную специализацию. Частичное - это еще что-то, и оно также невозможно для функций, только для структур / классов. Таким образом, поскольку вы не можете специализироваться
std::swap
на классах шаблонов, вы должны предоставить бесплатную функцию в своем пространстве имен. Неплохая вещь, если можно так выразиться. Теперь также возможна явная специализация, но, как правило, вы не хотите специализировать шаблон функции :
namespace std
{
template<>
void swap<Bar>(Bar& lhs, Bar& rhs) noexcept {
}
}
- Нет, поскольку 1) отличается от 2) и 3). Кроме того, наличие 2) и 3) всегда приводит к выбору 2), потому что он подходит лучше.
using std::swap;
не включает ADL, он просто позволяет компилятору определитьstd::swap
, не находит ли ADL подходящую перегрузку.Чтобы ответить на EDIT, где классы могут быть шаблонными, вам вообще не нужна специализация. рассмотрим такой класс:
template <class T> struct vec3 { T x,y,z; };
вы можете определять такие классы, как:
vec3<float> a; vec3<double> b; vec3<int> c;
если вы хотите иметь возможность создать одну функцию для реализации всех трех свопов (не то, чтобы этот примерный класс это гарантировал), вы делаете так же, как Xeo сказал в (2) ... без специализации, а просто создайте обычную функцию шаблона:
template <class T> void swap(vec3<T> &a, vec3<T> &b) { using std::swap; swap(a.x,b.x); swap(a.y,b.y); swap(a.z,b.z); }
Функция шаблона подкачки должна находиться в том же пространстве имен, что и класс, который вы пытаетесь поменять местами. следующий метод найдет и использует этот обмен, даже если вы не ссылаетесь на это пространство имен с помощью ADL:
using std::swap; swap(a,b);
источник
Кажется, что (2) ( свободное положение
swap
в том же пространстве имен, где объявлен определяемый пользователем класс ) является единственным допустимым способом предоставленияswap
определяемого пользователем класса, поскольку добавление объявлений в пространство именstd
обычно является неопределенным поведением. Расширение пространства имен std (cppreference.com) :И
swap
не обозначается как одно из таких исключений. Поэтому добавление собственнойswap
перегрузки вstd
пространство имен - это неопределенное поведение.Также сказано, что стандартная библиотека использует неквалифицированный вызов
swap
функции для вызоваswap
пользовательского класса для пользовательского класса, если такой пользовательскийswap
предоставляется.Возможность замены (cppreference.com) :
своп (www.cplusplus.com) :
Но обратите внимание, что прямое использование
std::swap
функции для пользовательского класса вызывает общую версиюstd::swap
вместо пользовательскойswap
:my::object a, b; std::swap(a, b); // calls std::swap, not my::swap
Поэтому рекомендуется вызывать
swap
функцию в пользовательском коде так же, как это делается в стандартной библиотеке:my::object a, b; using std::swap; swap(a, b); // calls my::swap if it is defined, or std::swap if it is not.
источник