как обеспечить функцию подкачки для моего класса?

87

Как правильно включить мои swapалгоритмы в STL?

1) Участник swap. Имеет ли std::swapиспользовать SFINAE трюк использовать элемент swap.

2) Свободное положение swapв том же пространстве имен.

3) Частичная специализация std::swap.

4) Все вышеперечисленное.

Спасибо.

РЕДАКТИРОВАТЬ: Похоже, я не сформулировал свой вопрос четко. По сути, у меня есть шаблонный класс, и мне нужны алгоритмы STL для использования (эффективного) метода подкачки, который я написал для этого класса.

pic11
источник

Ответы:

94
  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 ...
}
  1. Это правильный способ предоставить 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);
}

...
  1. Вы имеете в виду явную специализацию. Частичное - это еще что-то, и оно также невозможно для функций, только для структур / классов. Таким образом, поскольку вы не можете специализироваться std::swapна классах шаблонов, вы должны предоставить бесплатную функцию в своем пространстве имен. Неплохая вещь, если можно так выразиться. Теперь также возможна явная специализация, но, как правило, вы не хотите специализировать шаблон функции :
namespace std
{  // only allowed to extend namespace std with specializations

template<> // specialization
void swap<Bar>(Bar& lhs, Bar& rhs) noexcept {
    // ...
}

}
  1. Нет, поскольку 1) отличается от 2) и 3). Кроме того, наличие 2) и 3) всегда приводит к выбору 2), потому что он подходит лучше.
Xeo
источник
8
Ваш (1) и вопрос (1) на самом деле не совпадают, если я что-то не понимаю. Тем не менее, +1
Dennis Zickefoose
1
@Xeo. Спасибо за ваш вклад. Я отредактировал свой вопрос. Использует ли STL своп, как вы описали в случае 1?
pic11
1
@pic: Да, STL будет использовать своп ADL, который я показал в 1), но только если он присутствует как бесплатная функция, а не только как функция-член. См. 2) и 3), обе версии будут выбраны алгоритмами. Я бы посоветовал 2), так как 3) устарел и считается плохой практикой.
Xeo
2
Комментарий в первом фрагменте кода вводит в заблуждение. using std::swap;не включает ADL, он просто позволяет компилятору определить std::swap, не находит ли ADL подходящую перегрузку.
Дэвид Родригес - дрибэас
6
Этот ответ является технически правильным, но очень нуждается в редактировании для ясности. OP (1) не является правильным ответом, поскольку слишком быстрое чтение этого ответа, похоже, ошибочно указывает.
Ховард Хиннант
1

Чтобы ответить на 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);
Бен
источник
0

Кажется, что (2) ( свободное положение swapв том же пространстве имен, где объявлен определяемый пользователем класс ) является единственным допустимым способом предоставления swapопределяемого пользователем класса, поскольку добавление объявлений в пространство имен stdобычно является неопределенным поведением. Расширение пространства имен std (cppreference.com) :

Неопределенное поведение заключается в добавлении объявлений или определений в пространство имен stdили в любое пространство имен, вложенное в него std, за некоторыми исключениями, указанными ниже.

И swapне обозначается как одно из таких исключений. Поэтому добавление собственной swapперегрузки в stdпространство имен - это неопределенное поведение.

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

Возможность замены (cppreference.com) :

Многие стандартные библиотечные функции (например, многие алгоритмы) ожидают, что их аргументы будут соответствовать Swappable , что означает, что каждый раз, когда стандартная библиотека выполняет обмен, она использует эквивалент using std::swap; swap(t, u);.

своп (www.cplusplus.com) :

Многие компоненты стандартной библиотеки (в 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.
anton_rh
источник