Подтвердить что ты не робот

Как реализовать swap()?

Возможный дубликат:
как обеспечить функцию свопинга для моего класса?

Каждый раз, когда я думаю, что понимаю, я вижу кое-что, что меня действительно смущает.

Если вы хотите предоставить реализацию swap для своего собственного класса, что вы делаете?

Список возможностей:

Мое собственное понимание заключается в том, что мне нужны # 5 и # 3, где вызывающий абонент будет вызывать swap как using std::swap; swap(a, b);, но тот факт, что никто, кажется, не предполагает, что эта комбинация действительно меня смущает. И я действительно не понимаю № 4 вообще, потому что все, кажется, используют член экземпляра, когда на самом деле операция статична. Я не могу сказать, не ошибается ли мое понимание или кучу ответов, которые я вижу при просмотре.

Каков правильный путь?

4b9b3361

Ответ 1

Общая картина, которую я видел, - это 3 и 5, согласно вашему собственному пониманию.

  • добавляет специализацию в пространство имен std::, которое разрешено, но может быть невозможно во всех случаях (если ваш тип является самим шаблоном).
  • не дает никаких преимуществ и не дает возможности квалифицироваться с типом при использовании вне одного из членов, что означает, что для реализации swap для других типов, которые содержат ваш тип в качестве члена, им необходимо будет квалифицировать вызов (void swap( other& l, other& r ) { T::swap( l.t, r.t ); })
  • не нуждается в дружбе, позволяет использовать с rvalue (даже в С++ 03) и в некоторых случаях идиоматично std::vector<int>().swap( v ); очищать содержимое вектора.
  • Что? Вы неправильно поняли код! Это не объявляет член, принимающий два аргумента, а скорее свободную функцию, принимающую два аргумента, и определяет функцию inline. Это эквивалентно 5 (без пересылки на 3, а скорее реализует все в свободной функции).
  • Свободная функция в том же пространстве имен позволяет ADL ее находить и позволяет другому коду использовать общий шаблон void swap( other& l, other& r ) { using std::swap; swap( l.t, r.t ); }, не зная, имеет ли тип other::t конкретную перегрузку swap или одну в std::. Пересылка в 3 позволяет вам предоставить единую (реальную) реализацию, которая может использоваться через ADL, а также на временных объектах.

Ответ 2

Как вы говорите, вам нужна # 5 (функция в том же пространстве имен, что и ваш тип) для поддержки идиоматического using std::swap; swap(a,b);. Таким образом, ваша перегрузка будет выбрана зависимым от аргумента поиска, вместо std::swap.

Если ваша реализация swap требует доступа к типу privates, вам нужно либо вызвать функцию-член, как # 3, либо объявить не-членную функцию a friend. Это то, что делают ваши примеры в # 4: функция friend может быть объявлена ​​и определена внутри класса, но это не делает ее членом; он по-прежнему находится в пределах области окружающего пространства.

Итак, это:

class thing {
    friend void swap(thing & a, thing & b) {/*whatever*/}
};

эквивалентно этому (более или менее - см. комментарии):

class thing {
    friend void swap(thing & a, thing & b);
};

inline void swap(thing & a, thing & b) {/*whatever*/}

# 1 (специализируется на std::swap для вашего типа), но некоторые считают его чистым, чтобы все было в вашем собственном пространстве имен.

Ни # 2, ни 3 не позволят неквалифицированному swap(a,b) найти вашу реализацию.

Ответ 3

В стандарте С++ 11 объект t заменяется с объектом u, если swap(t, u) и swap(u, t) являются допустимыми выражениями, которые выбирают не-членные функции, называемые swap, из набора перегрузки, который включает два шаблона std::swap в <utility> и любые перегрузки, найденные с помощью зависимого от аргумента поиска, так что меняются значения t и u.

Обычный способ обмена двумя объектами в описанных выше условиях:

using std::swap;
swap(t, u);

Здесь поиск имени рассмотрит std::swap и любые перегрузки swap, найденные ADL, тогда разрешение перегрузки выберет наилучшее соответствие.

Учитывая каждую из ваших реализаций:

  • Нельзя добавлять перегрузки в пространство имен std, и вышеприведенное правило не требует их поиска в любом случае. Целесообразно специализировать стандартные шаблоны функций std::swap, если специализация предназначена для определенного пользователем типа (т.е. Не является стандартным типом библиотеки или базовым типом.) Если вы специализируетесь на std::swap, это действительно, в противном случае нет, t частично специализировать шаблоны функций.

  • Статическая функция-член не будет найдена с помощью swap(t, u), потому что она должна быть квалифицирована, например. Foo::swap(t, u)

  • Унарная функция-член swap не может быть вызвана как swap(t, u), ее нужно вызывать как t.swap(u)

  • Связи, которые вы показываете, показывают функцию, отличную от члена friend, которая может быть найдена ADL, поэтому ее можно использовать для создания типов подкачки.

  • Такая функция может быть найдена ADL.

Итак, 2 и 3 не делают тип с возможностью замены. 4 делает, и 5 делает. 1 может сделать, если все сделано правильно, но не может использоваться для замены шаблонов классов.

3 не требуется, но может использоваться, чтобы помочь реализовать либо 4, либо 5, потому что функция-член будет иметь доступ к внутренним данным типа, поэтому может свопиться к приватным членам. Для 4 функция является другом, поэтому имеет доступ уже. Таким образом, по процессу устранения вам нужно либо 4, либо 3 и 5. Обычно считается, что лучше предоставить членский обмен (3), а затем предоставить не-членную функцию в том же пространстве имен (5), который вызывает своп членов.