Есть две разные вещи, о которых нужно беспокоиться. Прежде всего, есть точка зрения на язык. Спецификации языка, такие как стандарт (ы) 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 действительны в обстоятельствах, которые вы ожидаете от них.
Вы хотите прочитать раздел 12.2 стандарта: http://open-std.org/JTC1/SC22/WG21/docs/papers/2015/n4567.pdf (стр. 270). –