Почему вы используете std :: move, когда у вас есть && в C ++ 11?

Возможное дублирование:
Что такое семантика перемещения?

Недавно я посетил семинар по C ++ 11, и мне был дан следующий лакомый совет.

when you have && and you are unsure, you will almost always use std::move

Может ли кто-нибудь объяснить мне, почему вы должны использовать std::move в отличие от некоторых альтернатив и некоторых случаев, когда вам не следует использовать std::move?


person pyCthon    schedule 23.01.2013    source источник
comment
@XavierHolt Просто ударь меня: не логический оператор.   -  person Michael Todd    schedule 23.01.2013
comment
Вы определенно хотите посмотреть Скотт Мейерс - Универсальные справочники в C ++ 11.   -  person fredoverflow    schedule 23.01.2013
comment
Потому что названные ссылки rvalue на самом деле являются lvalue.   -  person anilbey    schedule 05.07.2018


Ответы (4)


Во-первых, вероятно, в вопросе, который я рассмотрю, есть заблуждение:
Когда вы видите T&& t в коде (а T - это фактический тип, а не тип шаблона), имейте в виду, что категория значений t - это lvalue (ссылка ), а не rvalue (временное). Это очень сбивает с толку. T&& просто означает, что t построен из объекта, который был rvalue 1, но t сам является lvalue, а не rvalue. Если у него есть имя (в данном случае t), то это lvalue и не будет автоматически перемещаться, но если у него нет имени (результат 3+4), то это rvalue и будет автоматически перейти к его результату, если может. Тип (в данном случае T&&) почти не имеет ничего общего с категорией значения переменной (в данном случае lvalue).

При этом, если в вашем коде написано T&& t, это означает, что у вас есть ссылка на переменную, которая была временной, и ее можно уничтожить, если вы хотите. Если вам нужно обращаться к переменной несколько раз, вы не хотите std::move из нее, иначе она потеряет свое значение. Но в последний раз, когда вы переходите t, безопасно std::move это значение для другого T, если хотите. (И в 95% случаев это то, что вы хотите делать). Все это относится и к auto&& переменным.

1. если T - тип шаблона, T&& - это ссылка переадресации, и в этом случае вы используете std::forward<T>(t) вместо std::move(t) в последний раз. См. этот вопрос.

person Mooing Duck    schedule 23.01.2013
comment
ах ха! вот что я искал в универсальном эталонном корпусе! - person pyCthon; 23.01.2013
comment
@pyCthon: для универсальных ссылок вы хотите использовать std::forward<T>(t) вместо std::move(t) в последний раз. - person Mooing Duck; 23.01.2013
comment
Следует иметь в виду, что если вы видите переменную с именем, тогда не имеет значения, объявлена ​​ли она как && (rvalue) & (ссылка) или `` (копия) - она ​​всегда считается lvalue. Вы можете думать об истинных ссылках на rvalue как о временном материале, который может быть возвращен только из функции (вашей собственной или заключенной в std :: move (), которая - в двух словах - возвращает аргумент). - person Red XIII; 23.01.2013
comment
@LucDanton: Я изменил формулировку и добавил дополнительное предложение, чтобы пояснить, что категория значения имеет мало общего с типом. Если это все еще не так, не стесняйтесь редактировать или отправлять мне лекцию в чат или что-то в этом роде. - person Mooing Duck; 24.01.2013
comment
@MooingDuck: Не могли бы вы уточнить (или опубликовать ссылку) в последней части (std :: forward vs std :: move для универсальных ссылок)? - person Macke; 30.01.2013
comment
@Macke: С универсальной ссылкой вы не знаете, является ли это rvalue или lvalue, поэтому вы не знаете, нужно ли вам std::move это или нет. std::forward то же самое, что std::move, если вход был rvalue, и ничего не делает, если вход был lvalue, поэтому вы всегда получаете правильный механизм. См. этот вопрос. - person Mooing Duck; 30.01.2013
comment
@MooingDuck: А. Понятно. Спасибо! :) - person Macke; 30.01.2013
comment
Не существует такой вещи, как универсальная ссылка, единственное, что существует, - это правила сворачивания ссылок. Возможно, захочется исправить ответ. - person user541686; 13.01.2014
comment
@Mehrdad Я не согласен, использование правил сворачивания ссылок очень часто называют универсальной ссылкой, почти так же, как динамически выделяемая память поступает из кучи. Это не официальная терминология, но это общее название. - person Mooing Duck; 13.01.2014
comment
Черт возьми, кажется, я единственный, кто не мог получить это от объекта, который был rvalue, вы можете объяснить Psyduck? - person gsamaras; 18.05.2015
comment
@ G.Samaras: rvalues ​​- это безымянные результаты выражений (функций и математических операций). Например, результат myintx + myinty сам по себе int, но не имеет имени, поэтому это будет rvalue. Вы можете передать это функции, ожидающей rvalue (&&), но вы не можете передать это функции, ожидающей ссылку lvalue (&). Точно так же вы не можете передать myintx функции, ожидающей rvalue (&&), потому что у нее есть имя. Чтобы отменить имя объекта, чтобы сообщить компилятору, что переместить этот объект безопасно, используйте функцию std::move. - person Mooing Duck; 18.05.2015
comment
Спасибо за четкое различие между двумя понятиями: тип и категория значения; трудно думать об этих вещах, не зная правильных терминов. - person legends2k; 26.06.2015
comment
T&& просто означает, что t может быть построен из объекта, который был rvalue. Разве это не значит, что он должен быть построен из rvalue? (и ofc lvalues ​​можно превратить в rvalue, например, через move) - person ben; 16.03.2017
comment
@ben: исправлено, спасибо. - person Mooing Duck; 16.03.2017
comment
Также все в форме auto && используйте std :: forward вместо std :: move. - person novieq; 27.11.2017
comment
@novieq: добавлено. - person Mooing Duck; 27.11.2017

Я нашел эту статью, чтобы дать достаточно информации о ссылках на rvalue в целом. Он упоминает std::move ближе к концу. Это, наверное, самая актуальная цитата:

Нам нужно использовать std::move, от <utility> - std::move - это способ сказать: «Хорошо, честно говоря, я знаю, что у меня есть lvalue, но я хочу, чтобы это было rvalue». std::move сам по себе ничего не перемещает; он просто превращает lvalue в rvalue, чтобы вы могли вызвать конструктор перемещения.


Допустим, у вас есть конструктор перемещения, который выглядит так:

MyClass::MyClass(MyClass&& other): myMember(other.myMember)
{
    // Whatever else.
}

Когда вы используете оператор other.myMember, возвращается значение lvalue. Таким образом, код использует конструктор copy для инициализации this->myMember. Но поскольку это конструктор перемещения, мы знаем, что other - временный объект, а значит, и его члены. Поэтому мы действительно хотим использовать более эффективный конструктор move для инициализации this->myMember. Использование std::move гарантирует, что компилятор рассматривает other.myMember как ссылку rvalue и вызывает конструктор перемещения, как вы хотите:

MyClass::MyClass(MyClass&& other): myMember(std::move(other.myMember))
{
    // Whatever else.
}

Только не используйте std::move на объектах, которые вам нужно держать рядом - конструкторы перемещения практически гарантированно уничтожат любые переданные в них объекты. Вот почему они используются только с временными конструкциями.

Надеюсь, это поможет!

person Xavier Holt    schedule 23.01.2013

Когда у вас есть объект типа T&&, rvalue, это означает, что этот объект можно безопасно перемещать, так как никто больше не будет зависеть от его внутреннего состояния позже.

Поскольку перемещение никогда не должно быть дороже копирования, вы почти всегда захотите переместить его. И чтобы переместить его, вы должны использовать функцию std::move.

Когда следует избегать std::move, даже если это будет безопасно? Я бы не стал использовать его в тривиальных примерах, например:

 int x = 0;
 int y = std::move(x);

В остальном я не вижу минусов. Если это не усложняет код, перемещение надо делать по возможности ИМХО.

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

return std::move(x); // not recommended

(Если вам повезет, сработает оптимизация возвращаемого значения, что даже лучше, чем операция перемещения.)

person Philipp Claßen    schedule 23.01.2013
comment
Скотт Мейерс утверждает, что вам следует перемещать даже примитивные типы. - person fredoverflow; 23.01.2013
comment
@FredOverflow Интересно. Объясняет ли он это в своем выступлении на C ++ и после 2012 года? Я могу думать об использовании этого только как о какой-то документации, но я не совсем уверен, что это хорошая идея. - person Philipp Claßen; 23.01.2013
comment
Он объясняет это в своем выступлении, на которое я ссылался как комментарий к вопросу. Он говорит, что вы должны делать это, даже не задумываясь об этом. Но то, что Скотт Мейерс говорит это, не означает, что это истина в последней инстанции, которую, конечно же, должен придерживаться каждый, не подвергая ее сомнению. - person fredoverflow; 23.01.2013

Вы можете использовать move, когда вам нужно «перенести» содержимое объекта в другое место без копирования. Также возможно, что объект получит содержимое временного объекта без копирования, с помощью std :: move.

Проверить эту ссылку

person Rahul Tripathi    schedule 23.01.2013
comment
Оформить заказ, эта ссылка не является подходящим ярлыком для ссылки. Можете ли вы улучшить его так, чтобы метка описывала объект, с которым связана связь? - person Lightness Races in Orbit; 11.01.2015