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


87

Який правильний спосіб увімкнути мої swapалгоритми STL?

1) Член swap. Чи std::swapвикористовує фокус SFINAE для використання учасника swap.

2) Вільне стояння swapв одному просторі імен.

3) Часткова спеціалізація std::swap.

4) Все вищесказане.

Дякую.

EDIT: Схоже, я не чітко сформулював своє запитання. В основному, у мене є шаблонний клас, і мені потрібні алгоритми STL, щоб використовувати метод (ефективного) обміну, який я написав для цього класу.

Відповіді:


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) вибрано, тому що це краще підходить.

8
Ваші (1) та запитання (1) насправді не співпадають, якщо я щось неправильно прочитав. Тим не менше, +1
Денніс Зікфуз,

1
@Xeo. Дякуємо за вхід. Я відредагував своє запитання. Чи використовує STL своп, як ви це описали у випадку 1?
pic11

1
@pic: Так, STL використовуватиме заміну ADL, яку я показав у 1), але лише якщо вона є як вільна функція, а не лише функція-член. Див. 2) та 3), обидві версії будуть обрані алгоритмами. Я б порадив 2), оскільки 3) застарів і вважається поганою практикою.
Xeo,

2
Коментар у першій частині коду вводить в оману. using std::swap;не вмикає ADL, він просто дозволяє компілятору знайти, std::swapякщо ADL не знаходить належного перевантаження.
Девід Родрігес - dribeas

6
Ця відповідь є технічно правильною, але надзвичайно потребує редагування для ясності. ОП (1) не є правильною відповіддю, оскільки надто швидке читання цієї відповіді, здається, помилково вказує.
Говард Хіннант,

1

Щоб відповісти на EDIT, де класи можуть бути шаблонами, вам зовсім не потрібна спеціалізація. розглянемо такий клас:

template <class T>
struct vec3
{
    T x,y,z;
};

Ви можете визначити такі класи, як:

vec3<float> a;
vec3<double> b;
vec3<int> c;

якщо ви хочете мати можливість створити одну функцію для реалізації всіх 3-х обмінів (не те, що цей приклад клас це виправдовує), ви робите так само, як сказав 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.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.