Думая о этой теме У меня возникла мысль (учитывая, что я говорю о C++11
), что определяемый пользователем swap
должен предоставляться только для типов, которые не предоставляют move semantic
, точнее оператор move assignment
(потому что я думаю, что более распространенным является move ctr
при отсутствии оператора move assignment
); или оператор move ctor
и / или оператор move assignment
имеют некоторые побочные эффекты, которые нежелательны.
Давайте посмотрим на примеры: [так же, как в связанной теме]
class remote_connection
{
public:
int socket_fd;
friend void swap( remote_connection& lhs, remote_connection& rhs ) = delete;
}
Поскольку НЕТ предоставил пользовательский swap
, то код, пытающийся поменять объекты типа remote_connection
, вызывает swap
, инстанцированный с std::swap
, что вызывает создание перемещения и два назначения перемещения (как @ 1201ProgramAlarm указано).
Таким образом, для обмена объектами выдает компилятор: один вызов функции для std::swap
, один вызов для move ctr
и два вызова для оператора move assign
. Что приводит к 3 int
копий, чтобы сделать реальный обмен.
В результате: 4 вызовов функций и 3 копий.
Давайте реализуем swap
метод:
class remote_connection
{
public:
int socket_fd;
friend void swap( remote_connection& lhs, remote_connection& rhs )
{
using std::swap;
swap( lhs.socket_fd, rhs.socket_fd );
}
}
Поскольку мы предоставили пользовательский swap
, код, пытающийся поменять объекты типа remote_connection
, вызывает этот пользовательский swap
, что вызывает 3 копии для int
(создание tmp из rhs, скопируйте lhs в rhs, скопируйте tmp в lhs).
Итак, чтобы поменять объекты, которые вызывает компилятор: один вызов функции swap
(найден ADL), один вызов std::swap(int&, int&)
. Что также приводит к 3 int
копий, чтобы сделать реальный обмен.
В результате: 2 вызовов функций и 3 копий.
Пользовательский своп выиграл, но что, если мы добавим несколько членов в наш класс:
class remote_connection
{
public:
int socket_fd;
int a, b, c, d, e;
friend void swap( remote_connection& lhs, remote_connection& rhs )
{
using std::swap;
swap( lhs.socket_fd, rhs.socket_fd );
swap( lhs.a, rhs.a );
swap( lhs.b, rhs.b );
swap( lhs.c, rhs.c );
swap( lhs.d, rhs.d );
swap( lhs.e, rhs.e );
}
}
Здесь мы имеем: 8 вызовов функций и 6 копий.
Но если бы мы не предоставили пользовательский swap
, у нас было бы: 4 вызовов функций и 6 копий.
Итак, кажется, что какой бы класс мы ни имели (производный от класса с множеством членов, состоящих из множества подобъектов встроенных и пользовательских типов), более разумно, ИМХО, не предоставлять пользовательский swap
. Не только это, но и такое swap
трудно поддерживать (если мы что-то изменили в классе и забыли отразить, что изменения в swap
мы получим сюрприз.)
Таким образом, возникает вопрос: «Если у нас нет никаких побочных эффектов в move ctr
/ move assign
operator
и мы обеспечиваем их обоих, нужно ли нам внедрять пользовательский swap
, или это хорошая практика, чтобы полагаться stl
предоставил один в таких обстоятельствах? "
Извините за такое количество , поэтому , я не являюсь носителем языка:)
P.S. Я забыл добавить, что нам нужен, IMHO, определенный пользователем swap
, если мы собираемся реализовать идиому copy-and-swap
, и мы реализовали оператор move assignment
.