использование свопа для реализации назначения перемещения

Кое-что пришло мне в голову, что я считаю вполне разумным, но я хотел бы узнать мнение людей об этом на случай, если я просто что-то упустил. Итак, во-первых, мое понимание T& operator=(T&& rhs) заключается в том, что нам не важно, что будет с содержимым rhs, когда мы закончим, просто содержимое будет перемещено в this, а rhs можно безопасно уничтожить.

При этом обычная реализация оператора копирования-присваивания, безопасная для исключений, при условии, что свопы дешевы, выглядит следующим образом:

T& operator=(const T& rhs) {
    if(this != &rhs) {
        T(rhs).swap(*this);
    }
    return *this;
}

Таким образом, один из естественных способов реализации оператора перемещения-присваивания будет выглядеть следующим образом:

T& operator=(T&& rhs) {
    if(this != &rhs) {
        T(std::move(rhs)).swap(*this);
    }
    return *this;
}

Но потом мне пришло в голову, что rhs не обязательно должно быть пустым! Итак, почему бы просто не сделать простой swap?

T& operator=(T&& rhs) {
    rhs.swap(*this); // is this good enough?
    return *this;
}

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

Единственный «недостаток», о котором я могу думать, заключается в том, что вещи, принадлежащие this, потенциально будут жить дольше, используя простой обмен, по сравнению с версией, которая выполняет перемещение-конструкцию/своп.

Мысли?


person Evan Teran    schedule 26.08.2015    source источник
comment
stackoverflow.com/q/9847860/560648?   -  person Lightness Races in Orbit    schedule 26.08.2015
comment
Я предпочитаю назначение перемещения в конце этого ответа: stackoverflow.com/a/3279550/4342498   -  person NathanOliver    schedule 26.08.2015
comment
обратите внимание, что вы можете выполнить оба обмена одновременно, используя T& operator=(T rhs) { rhs.swap(*this); return *this; }   -  person M.M    schedule 19.11.2015
comment
нас не волнует, что будет содержимым rhs, когда мы закончим да, нужно: он должен быть в допустимом состоянии для деструктора. Например, мы не должны хранить указатель для удаления, когда этот указатель был передан другому объекту.   -  person Walter    schedule 19.11.2015
comment
@ Уолтер. Вы должны прочитать все предложение, которое вы цитируете. В частности, часть после запятой, где я говорю ...только что содержимое было перемещено в this и что rhs можно безопасно уничтожить.   -  person Evan Teran    schedule 19.11.2015


Ответы (1)


"что должен делать оператор присваивания перемещения"

Я всегда нахожу, что лучший способ обращения с переменной && — это сказать, что «никого не волнует, в каком состоянии я оставлю эту переменную». Вы можете уйти от него. Вы можете копировать оттуда. Вы можете переключиться на него. Есть еще много вещей, которые вы можете сделать.

Ваш код правильный. && можно оставить в любом состоянии (при условии, что его можно разрушить).

Объект будет уничтожен рано или поздно (вероятно, раньше), и разработчик должен знать об этом. Это важно, если деструктор будет delete необработанных указателей, что приведет к двойному удалению, если один и тот же необработанный указатель находится в нескольких объектах.

person Aaron McDaid    schedule 26.08.2015
comment
Иногда вам нужно оставить rhs в согласованном состоянии. Например, при перемещении c'tor и назначении std::unique_ptr вы должны установить rhs.ptr в nullptr либо явно, либо с помощью вызова rhs.release(), чтобы предотвратить двойное удаление. - person Martin Trenkmann; 11.11.2015
comment
@MartinTrenkmann, может показаться, что ваш комментарий (непреднамеренно) подразумевает, что пользователям unique_ptr нужно будет вызывать release для него. Но это не так. unique_ptr реализован для правильной работы. - person Aaron McDaid; 17.11.2015
comment
@MartinTrenkmann, я думаю, что мой комментарий о том, что && можно оставить в любом состоянии, все еще остается в силе. Но я думаю, что разработчик должен знать, что объект && будет уничтожен (вероятно, довольно скоро), и поэтому разработчик должен учитывать побочные эффекты этого разрушения. - person Aaron McDaid; 17.11.2015
comment
@MartinTrenkmann, другими словами, я пытаюсь определить, считаете ли вы, что в моем ответе есть ошибка. И если да, то как мой ответ можно улучшить - person Aaron McDaid; 17.11.2015
comment
Вопрос касается реализации назначения перемещения и особенно состояния rhs, поэтому мой комментарий был с точки зрения реализаторов. В этом контексте ваше утверждение никого не волнует, в каком состоянии я оставляю эту переменную, неверно. С другой стороны, как пользователь чьего-то оператора перемещения или назначения, вы правы, вам не нужно заботиться о rhs, его просто больше нельзя использовать, но это не вопрос в мое мнение. - person Martin Trenkmann; 18.11.2015
comment
Я кое-что добавил к ответу, но он меня не совсем устраивает. Я пытаюсь подчеркнуть, что && не обязательно должно быть логически пустым. Нам просто нужно быть уверенными, что (неизбежный) вызов деструктора не будет иметь никаких неприятных побочных эффектов. - person Aaron McDaid; 19.11.2015
comment
Я думаю, это зависит от семантики класса; например, мы ожидаем, что unique_ptr<T> x,y; .... x = std::move(y); освободит старый объект в x. - person M.M; 19.11.2015
comment
никого не волнует, в каком состоянии я оставлю эту переменную неправильной. Состояние должно быть допустимым состоянием для деструктора. Например, вы не можете оставить указатель, указывающий на память, которая будет удалена, когда вы передадите этот адрес другому объекту. - person Walter; 19.11.2015
comment
@ Уолтер, именно это я и сказал в своем ответе. Даже первая версия моего ответа говорила (пока она разрушаема) - person Aaron McDaid; 19.11.2015
comment
Все, моя общая точка зрения, с которой, я уверен, мы все согласны, заключается в том, что не имеет значения (для корректности), является ли перемещаемый объект пустым (т. е. уничтожение ничего не делает) или является ли он глубоким копия целевого объекта (т.е. уничтожение по-прежнему не имеет нежелательных побочных эффектов). Это тот тип ответа, который необходим для исходного вопроса. Не стесняйтесь опубликовать другой ответ, если вы думаете, что я плохо выразил это мнение - person Aaron McDaid; 19.11.2015
comment
@AaronMcDaid Да, вы говорите это, но только позже и в скобках, а не в исходном неправильном утверждении. Вы должны уточнить это утверждение. - person Walter; 19.11.2015