Имеет ли смысл использовать семантику перемещения для оператора + и / или оператора + =? - PullRequest
6 голосов
/ 24 марта 2012

Мне было интересно, в каких случаях имеет смысл использовать семантику перемещения при перегрузке оператора + и / или оператора + =.Хотя в этом вопросе объясняется, как можно это сделать, я не могу понять, почему я это сделал.Давайте рассмотрим оператор + =.Если я просто передам правую часть по ссылке и внесу соответствующие изменения в объект левой стороны, то все равно ненужных копий не будет.Итак, мы возвращаемся к одной и той же точке: будет ли семантика перемещения полезной в таком случае?

Ответы [ 2 ]

12 голосов
/ 24 марта 2012

Да и нет.

оператор + =

Семантика перемещения не всегда полезна для operator+= в целом, потому что вы уже модифицируете левый аргумент (this), поэтому у вас уже есть ресурсы для работы в большинстве случаев.

Тем не менее, как оптимизация, это может стоить того. Представьте себе реализацию std::string, конструктор по умолчанию которой не выделяет никакой памяти. Тогда std::string::operator+=(std::string&&) может просто украсть ресурсы у RHS. Или представьте, что буфер RHS достаточно большой, чтобы вместить все, а LHS - нет, тогда, если вы можете использовать буфер RHS, то у вас все в порядке: просто поменяйте местами и добавьте.

Итак, это может стоить того, но вы должны изучить это. Поэтому:

  • T& T::operator+=(T const&): всегда присутствует
  • T& T::operator+=(T&&): включить семантику перемещения, когда это имеет смысл

оператор +

Здесь это всегда полезно (при условии, что мы говорим о классах, для которых полезна семантика перемещения).

Дело в том, что operator+ создает временное (неожиданно), поэтому обычно для этого временного необходимо создать ресурсы . Однако, если он может украсть их, а не создать, это, безусловно, дешевле.

Однако не обязательно указывать все перегрузки:

  • T operator+(T const&, T const&)
  • T operator+(T&&, T const&)
  • T operator+(T const&, T&&)
  • T operator+(T&&, T&&) (требуется для устранения неоднозначности)

Нет, вы можете использовать тот же трюк, который используется operator=, и создать временное право в сигнатуре функции (взяв один аргумент за копию). Если тип является перемещаемым, будет вызван конструктор перемещения, в противном случае это будет конструктор копирования, но так как вам все равно нужен временный объект, без потери производительности.

inline T operator+(T left, T const& right) { left += right; return left; }
inline T operator+(T const& left, T right) { right += left; return right; } // commutative
inline T operator+(T left, T&& right) { left += right; return left; } // disambiguation

Не много выигрыша (3 вместо 4), но я возьму все, что смогу!

Конечно, для строки, operator+ не является коммутативным (вот почему это плохая перегрузка), поэтому для фактической реализации второй перегрузки потребуется prepend метод.

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

inline T operator+(T left, T const& right) { left += right; return left; }
inline T operator+(const T& left, T&& right) { right += left; return right; }

С другой стороны, похоже, это работает только для коммутативных операций; - не работает таким образом, но, вероятно, может быть адаптировано, / и % с другой стороны ...

1 голос
/ 24 марта 2012

Если вы добавляете две строки, векторы и т. Д., Которые вы не можете «переместить», это не имеет смысла.Но если вы добавляете, скажем, связанные списки, где добавление списка, возможно, является оператором O (1), если вы готовы пожертвовать правой стороной, то это имеет смысл.

...