Как обеспечить функцию подкачки для моего класса?
Как правильно включить мой swap
в алгоритмах STL?
1) Член swap
, Есть ли std::swap
использовать трюк SFINAE, чтобы использовать член swap
,
2) Свободное положение swap
в том же пространстве имен.
3) Частичная специализация std::swap
,
4) Все вышеперечисленное.
Спасибо.
РЕДАКТИРОВАТЬ: Похоже, я не сформулировал свой вопрос четко. По сути, у меня есть шаблонный класс, и мне нужны алгоритмы STL для использования (эффективного) метода подкачки, который я написал для этого класса.
2 ответа
1) правильное использование swap
, Пишите это таким образом, когда вы пишете код "библиотеки" и хотите включить ADL (поиск, зависящий от аргументов) в swap
, Кроме того, это не имеет ничего общего с SFINAE.
// some algorithm in your code
template<class T>
void foo(T& lhs, T& rhs){
using std::swap; // enable 'std::swap' to be found
// if no other 'swap' is found through ADL
// some code ...
swap(lhs, rhs); // unqualified call, uses ADL and finds a fitting 'swap'
// or falls back on 'std::swap'
// more code ...
}
2) Является ли правильный способ обеспечить swap
функция для вашего класса.
namespace Foo{
class Bar{}; // dummy
void swap(Bar& lhs, Bar& rhs){
// ...
}
}
Если swap
теперь используется, как показано в 1), ваша функция будет найдена. Кроме того, вы можете сделать эту функцию другом, если вам абсолютно необходимо, или предоставить члена swap
это вызывается свободной функцией:
// version 1
class Bar{
public:
friend void swap(Bar& lhs, Bar& rhs){
// ....
}
};
// version 2
class Bar{
public:
void swap(Bar& other){
// ...
}
};
void swap(Bar& lhs, Bar& rhs){
lhs.swap(rhs);
}
3) Вы имеете в виду явную специализацию. Частичное - это еще что-то, что также невозможно для функций, только структуры / классы. Таким образом, так как вы не можете специализироваться std::swap
для шаблонных классов вы должны предоставить бесплатную функцию в вашем пространстве имен. Неплохо, если можно так сказать. Теперь также возможна явная специализация, но обычно вы не хотите специализировать шаблон функции:
namespace std
{ // only allowed to extend namespace std with specializations
template<> // specialization
void swap<Bar>(Bar& lhs, Bar& rhs){
// ...
}
}
4) Нет, поскольку 1) отличается от 2) и 3). Кроме того, наличие 2) и 3) всегда приводит к выбору 2), потому что он подходит лучше.
Кажется, что (2)swap
в том же пространстве имен, где объявлен определенный пользователем класс) - единственный допустимый способ swap
для пользовательского класса, потому что добавление объявлений в пространство имен std
как правило, неопределенное поведение. Расширение пространства имен std (cppreference.com):
Это неопределенное поведение для добавления объявлений или определений в пространство имен
std
или в любое пространство имен, вложенное вstd
с несколькими исключениями, отмеченными ниже
А также swap
не обозначается как одно из тех исключений. Так что добавив свой swap
перегрузка на std
Пространство имен - это неопределенное поведение.
Также сказано, что стандартная библиотека использует неквалифицированный вызов swap
функция для вызова пользовательского swap
для пользовательского класса, если такой пользовательский swap
предоставлен.
Многие стандартные библиотечные функции (например, многие алгоритмы) ожидают, что их аргументы удовлетворят Swappable, что означает, что каждый раз, когда стандартная библиотека выполняет своп, она использует эквивалент
using std::swap; swap(t, u);
,
Многие компоненты стандартной библиотеки (в пределах
std
) вызовswap
неквалифицированным образом, чтобы разрешить вызывать пользовательские перегрузки для неосновных типов вместо этой универсальной версии: пользовательские перегрузкиswap
объявленные в том же пространстве имен, что и тип, для которого они предоставляются, выбираются с помощью зависимого от аргумента поиска в этой универсальной версии.
Но обратите внимание, что непосредственно с помощью 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.
Чтобы ответить на 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);