2016-02-26 1 views
5

Идея семантики перемещения состоит в том, что вы можете захватить все, начиная с другого временного объекта (с ссылкой на ссылку rvalue) и сохранить это «все» в вашем объекте. Это помогает избежать глубокого копирования, где достаточно одного построения вещей, поэтому вы строите вещи в объекте rvalue, а затем просто переносите их на свой длинный живой объект.Почему объекты C++ lvalue не могут быть привязаны к rvalue ссылкам (&&)?

Почему C++ не позволяет привязывать объекты lvalue к rvalue-ссылкам? Оба позволяют мне изменить ссылочный объект, поэтому для меня нет никакой разницы в доступе к внутренностям ссылочного объекта.

Единственная причина, по которой я могу догадаться, - это перегрузка функций неоднозначности.

+1

Вы спрашиваете, почему у вас нет 'void foo (vector & bar) {} foo (создайте вектор temp здесь);'? – NathanOliver

+1

Знаете ли вы 'std :: move'? – Jarod42

ответ

15

Но почему C++ не позволяет привязывать объекты lvalue к значениям rvalue?

Предполагая, что вы имеете в виду "Почему не C++ позволяет связывание RValue ссылки на Lvalue объектов": он делает. Это просто не автоматическое, поэтому вы должны использовать std::move, чтобы сделать его явным.

Почему? Потому что в противном случае вызов безобидной функции может неожиданно уничтожить то, что вы не ожидали его:

Class object(much,state,many,members,wow); 
looks_safe_to_me(object); 
// oh no, it destructively copied my object! 

VS.

Class object(much,state,many,members,wow); 
obviously_destructive(std::move(object)); 
// same result, but now the destruction is explicit and expected 

Записка о деструктивном копировании: почему я говорю деструктивно и уничтожение выше, я не имею в виду, что деструктор объекта заканчивает свою жизнь: просто его внутреннее состояние было перемещено в новый экземпляр. Это по-прежнему действительный объект, но он больше не имеет того же дорогостоящего состояния, которым он пользовался.


Замечание по терминологии: давайте посмотрим, если мы можем прояснить неточное использование именующее, Rvalue и т.д. выше.

Цитируя cppreference для потомков:

  • lvalue является

    выражение, которое имеет идентичность и не могут быть перемещены из.

    Таким образом, нет такого понятия, как объект Lvalue, но есть объект, который локально по имени (или называют) с помощью Lvalue выражения

  • rvalue является

    в выражение, которое является либо значением prvalue, либо значением x. Это можно переместить с. Он может иметь или не иметь идентичности.

    • prvalue (чистый Rvalue) примерно выражение со ссылкой на не-имени временного объект: мы не можем преобразовать наше выражение именующего к одному из этого IIUC.

    • xvalue (истекающее значение)

      выражение, которое имеет идентичности и может быть перемещен из.

      , который явно включает в себя результат std::move

Так что на самом деле происходит:

  • объект существует
  • объект идентифицируется локально с помощью Lvalue выражения, из которого нельзя перенести (чтобы защитить нас от неожиданных побочных эффектов)
  • std::move дает выражение xvalue (который может быть перемещен с) со ссылкой на тот же объект как-значение выражения
  • это означает, что объекты, такие как переменные (которые называются по-значение выражения) не может быть неявно перемещается из, и должен вместо этого явно переместился через явное выражение xvalue, такое как std::move.
  • анонимные временные конструкции, вероятно, уже ссылается prvalue выражений, и могут быть перемещены неявно
+1

Когда вы используете 'std :: move', вы не привязываете ссылку rvalue к lvalue. Вы меняете lvalue на rvalue, а затем привязываете ссылку rvalue к этому. –

+0

Не является промежуточным выражением xvalue? В любом случае, конечным результатом является ссылочная ссылка rvalue, на которой мы ранее идентифицировали объект как lvalue ... – Useless

+0

@Useess Но объекты не имеют категорий значений, выражения имеют. Выражение 'object' является значением lvalue, выражением' std :: move (object) 'в rvalue. – TartanLlama

3

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

Предоставление как rvalues, так и lvalues ​​для привязки к значению lvalue делает невозможным.

Следовательно, значения, привязанные к ссылке rvalue, могут быть перемещены из (необязательно всегда будут перемещены, но разрешены), а lvalues ​​могут быть привязаны к lvalue-ссылкам и не могут быть перенесены.

std::move Здесь допускается отливка между категориями значений (до значения r), чтобы позволить перемещению.

Примечание; Ссылки на константу lvalue (const T&) могут быть связаны как с rvalues ​​(временными), так и с lvalues, поскольку упомянутый объект не может измениться (он отмечен как const, так что от него не может быть никакого перемещения).

Существует некоторая история (назад к ранним дням C++), почему временные объекты не могут быть привязаны к не-const lvalue-ссылкам, чтобы начать с ... деталь размыта, но было несколько соображений, что изменение временный didn't make sense, так как он все равно будет уничтожать в конце текущего заявления. Кроме того, вас можно убаюкивать в том смысле, что вы изменяли lvalue, когда вы там, где на самом деле нет - семантика кода могла/была бы неправильной и была бы ошибкой.There are further reasons привязан к адресам, литералам и т. Д. Это было до перемещения, а семантика его затвердела и является одной из мотиваций для движения и ее семантики.

+0

Мое общее впечатление заключается в том, что, пытаясь исправить что-то, комитет C++ просто создал больше беспорядка. До или 'T &&' рассказ можно было использовать ссылки для изменения переданных объектов (неважно, какие изменения происходят). Это означает, что в любой ситуации, когда передается не-const-ссылка, необходимо проверить, что происходит с переданным объектом. На самом деле они пытаются создать представление о том, что 'T &' не следует изменять (т. Е. Его содержимое не следует принимать). Понятие плохое. Для этого следует использовать 'const T &'. –

+0

Чистый эффект их сложных правил будет противоположным. Много багги кода будет написано, потому что многие программисты вместо того, чтобы пытаться понять мельчайшие детали дизайна языка, напишут «что-то» как-то ». Да, иногда их код будет работать. В других случаях - как обычно. –

Смежные вопросы