2016-03-26 2 views
3

rvalue ссылки: что такое «временные» объекты, каков их объем и где они хранятся?rvalue ссылки: что конкретно представляют собой «временные» объекты, каков их объем и где они хранятся?

Чтение несколько статей, rvalues ​​всегда определяются как «временные» объекты, такие как Animal(), где Animal является класс, или какой-то буквальное например 10.

Однако каково формальное определение rvalues ​​/ "временных" объектов?

Is new Animal() также считается «временным» объектом? Или это только значения в стеке, такие как Animal() и литералы, хранящиеся в коде?

Кроме того, где хранятся эти «временные» объекты, какова их область действия, и , как долго ссылки на эти значения действительны?

+2

Вы хотите прочитать раздел 12.2 стандарта: http://open-std.org/JTC1/SC22/WG21/docs/papers/2015/n4567.pdf (стр. 270). –

ответ

5

Во-первых, важно не ограничивать термины «rvalue» и «временный объект». У них очень разные значения.


Временные объекты не имеют длительность в хранения. Вместо этого у них есть правила жизни, характерные для временных объектов. Их можно найти в разделе [class.temporary] стандарта C++; существует summary on cppreference, который также включает список выражений, создающих временные объекты.

На практике я ожидаю, что компилятор либо оптимизирует объект, либо сохранит его в том же месте, где хранятся автоматические объекты.

Обратите внимание, что «временный объект» относится только к объектам типа класса. Эквивалент для встроенных типов называется значениями. (Не «временные значения»). Фактически термин «значения» включает как значения встроенного типа, так и временные объекты.

«Значение» - это совершенно отдельная идея для prvalue, xvalue, rvalue. Сходство в написании несчастливо.

Ценности не имеют объема. Область является собственностью . Во многих случаях область имени совпадает с временем жизни объекта или значения, которое оно называет, но не всегда.


Термины RValue, именующее т.д. значение категории выражения. Опишите выражения, а не значения или объекты.

Каждое выражение имеет значение категории. Кроме того, каждое выражение имеет значение , за исключением выражений типа void. Это две разные вещи. (Значение выражения имеет не ссылочный тип.)

Выражение категории стоимости rvalue может обозначать временный объект или не временный объект или значение встроенного типа.

выражение, которые создают временного объекта все имеет значение категория prvalue, однако тогда можно формировать выражения с категорией Lvalue которые обозначают тот же временный объект. Например:

const std::string &v = std::string("hello"); 

В этом случае v является именующего, но он обозначает временный объект. Время жизни этого временного объекта соответствует времени жизни v, как описано в предыдущей ссылке cppreference.

Link to further reading about value categories


Ссылка на Rvalue является ссылкой, который может связываться только с выражением категории значений RValue.(Сюда входят prvalue и xvalue). Слово rvalue в своем названии обозначает то, что оно связывает с, а не его собственной ценовой категорией.

Все названные ссылки действительно имеют категорию lvalue. После привязки нет разницы в поведении между ссылкой rvalue и ссылкой lvalue.

std::string&& rref = std::string("hello"); 

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

Другой пример:

std::string s1("hello"); 
std::string&& rref = std::move(s1); 
std::string& lref = s1; 

В этом случае rref является именующим, и это обозначает, не временный объект. Кроме того, lref и rref (и даже s1!) Все неотличимы отсюда, за исключением, в частности, результата decltype.

+0

Спасибо за ваш ответ. Это очень хорошо. Однако, как долго действительна ссылка rvalue? Нормальная ссылка действительна до тех пор, пока значение lvalue не выйдет за пределы области видимости? – Shuzheng

+0

Ссылка действительна до тех пор, пока объект (или значение), на который он ссылается, не закончил свою жизнь. –

+0

lvalues ​​не имеют области действия - имена имеют область действия. –

4

Есть две разные вещи, о которых нужно беспокоиться. Прежде всего, есть точка зрения на язык. Спецификации языка, такие как стандарт (ы) C++, не говорят о таких вещах, как регистры процессора, когерентность кэша, стеки (в смысле сборки) и т. Д. Тогда есть точка зрения реальной машины. Instruction set architectures (ISA), такие как единица (ы), определяемая Intel manuals, относятся к этому вопросу. Это, конечно, из-за переносимости и абстракции. Нет никакой веской причины для того, чтобы C++ зависела от специфических для x86 деталей, но много плохих. Я имею в виду, представьте, если HelloWorld.cpp будет только скомпилировать для вашей конкретной модели Core i7 без всякой причины! В то же время иногда вам нужен процессор. Например, как бы вы выпустили инструкцию CLI портативным способом? У нас разные языки, потому что нам нужно решить разные задачи , и у нас есть разные ISA, потому что нам нужно другое означает, чтобы решить их. Есть веская причина, объясняющая, почему ваш смартфон не использует процессор Intel, или почему ядро ​​Linux написано на C, а не ... Brainfuck.

Теперь, с точки зрения языка зрения, «Rvalue» это временное значение, время жизни которых заканчивается в выражении она оценивается в.

На практике rvalues ​​реализуются точно так же, как автоматические переменные, которые является, сохраняя их значение в стеке, или регистр, если компилятор видит, что он подходит. В случае автоматической переменной компилятор не может хранить его в регистре, если его адрес берется где-то в программе, потому что регистры не имеют «адреса». Однако, если его адрес никогда не принимается, и не задействован материал volatile, тогда оптимизатор компилятора может поместить эту переменную в регистр для оптимизации. Для rvalues ​​это всегда так, потому что вы не можете принять адрес rvalue. С точки зрения языка у них их нет (Примечание: Я использую старую терминологию C здесь, см. Комментарии для деталей, так как существует слишком много ошибок C++ 11, чтобы комментировать здесь). Это необходимо для правильной работы некоторых вещей. Например, cdecl требует, чтобы небольшие значения возвращались в регистр EAX. Таким образом, все вызовы функций должны оцениваться в rvalue (рассматривать ссылки как указатели для простоты), потому что вы не можете принимать адрес регистра, так как у них их нет!

Существует также концепция «жизни». С точки зрения языка, когда срок жизни какого-либо объекта «заканчивается», он перестает быть периодом.Когда это делает «начинается» и «заканчивается» зависит от распределения объекта означает:

  • Для объектов с динамической памятью, их жизнь sexplicitly начать с помощью new выражений и явно заканчивается с помощью delete заявлений. Этот механизм позволяет им пережить их первоначальный объем (например: return new int;).
  • Для объектов с автоматическим хранением их срок службы начинается, когда их объем достигнут в потоке программы и заканчивается, когда их объем выходит из системы.
  • Для объектов со статическим хранением их срок службы начинается до main() вызывается и заканчивается один раз main() выходов.
  • Для объектов с локальным хранилищем потоков срок их жизни начинается, когда начинается их соответствующая нить, и заканчивается, когда их соответствующая нить выходит.

Строительство и уничтожение, соответственно, участвуют в жизни объекта «начало» и «конец».

С точки зрения реальной машины биты всего лишь биты, период. Нет никаких «объектов», но бит и байтов в ячейках памяти и регистры CPU. Для таких вещей, как int, то есть POD type, «заканчивающийся на всю жизнь» означает совершенно ничего не делать. Для нетривиально разрушаемых не-POD-типов деструктор должен быть вызван в нужный момент. Тем не менее, память/регистр, который когда-то содержал «объект», все еще существует. Просто случается, что теперь его можно снова использовать чем-то другим.

Является ли новый Animal() также «временным» объектом? Или это только значения в стеке, такие как Animal() и литералы, хранящиеся в коде?

new Animal() выделяет память в куче для Animal, строит его, и все выражение в Animal*. Такое выражение является самой rvalue, поскольку вы не можете сказать что-то вроде &(new Animal()). Однако выражение оценивается как указатель, нет? Такой указатель указывает на lvalue, так как вы можете сказать такие вещи, как &(*(new Animal())) (будет протекать, хотя). Я имею в виду, если есть указатель, содержащий его адрес, то имеет адрес, нет?

Кроме того, где хранятся эти «временные» объекты, какова их область действия и как долго имеют значения rvalue для этих значений?

Как объяснялось выше, область действия «временного объекта» - это область действия, которая ее охватывает. Например, в выражении a(b * c) (предполагая, что a является функцией, принимающей ссылку на rvalue в качестве ее единственного аргумента), b * c - это значение r, значение которого заканчивается после того, как выражение, охватывающее его, то есть A(...), оценивается. После этого все оставшиеся ссылки rvalue на него, которые функция a может каким-то образом создать из своего параметра, свисают и заставят вашу программу делать забавные вещи. Для слов, если вы не злоупотребляете std::move или делаете другие вуду с rvalues, ссылки rvalue действительны в обстоятельствах, которые вы ожидаете от них.

+0

«scope» относится к * именам *, а не к объектам. Часто область имени совпадает с временем жизни объекта, который он называет, но не всегда. В вашем ответе, в большинстве случаев вы говорите «область действия», которую вы описываете «жизнь». –

+0

В вашем последнем абзаце, на каких ссылочных ссылках вы ссылаетесь? –

+0

@ M.M: WRT rvalue ссылки в последнем абзаце: Это было в ответ на побочный вопрос, сделанный OP. Будет редактировать, чтобы использовать лучшую формулировку, как вы предложили в своем первом комментарии. – 3442

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