Осуществимость пользовательского метода обмена - PullRequest
0 голосов
/ 28 апреля 2018

Думая о этой теме У меня возникла мысль (учитывая, что я говорю о 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.

...