Прошу прощения за широту вопроса, просто все эти детали тесно взаимосвязаны ..
Я пытался понять разницу между конкретно двумя категориями значений — xvalues и prvalues, но все же я запутался.
Так или иначе, ментальная модель, которую я пытался разработать для себя для понятия «идентичность», состоит в том, что выражение, которое оно имеет, должно гарантированно находиться в памяти данных самой программы.
По этой причине строковые литералы являются lvalues, они гарантированно находятся в памяти на протяжении всего выполнения программы, в то время как числовые литералы являются prvalue и могут, например, гипотетически храниться в прямой ассм.
То же самое относится и к std::move
из буквального значения, т.е. при вызове fun(1)
мы получили бы только параметр lvalue в кадре вызываемого абонента, но при вызове fun(std::move(1))
xvalue «вид» glvalue должен быть сохранен в кадре вызывающего.
Однако эта ментальная модель не работает, по крайней мере, с временными объектами, которые, как я понимаю, всегда должны создаваться в реальной памяти (например, если функция вызова значения rvalue называется как fun(MyClass())
с аргументом prvalue). Так что я думаю, что эта ментальная модель неверна.
Итак, как правильно относиться к свойству «идентичности» значений xvalue? Я прочитал это с идентичностью, я могу сравнить адреса, но если бы я мог сравнить адреса 2 MyClass().member
s (xvalue в соответствии с cppreference), скажем, передавая их через rvalue refs в некоторую функцию сравнения, тогда я не понимаю, почему я не могу сделать то же самое с 2 MyClass()
s (prvalue)?
Еще один источник, связанный с этим — вот ответ:
Что такое семантика перемещения?
Обратите внимание, что хотя std :: move (a) является значением r, его оценка не создает временный объект. Эта загадка вынудила комитет ввести третью категорию стоимости. То, что может быть связано со ссылкой на rvalue, даже если оно не является rvalue в традиционном смысле, называется xvalue (значение eXpiring).
Но это, похоже, не имеет ничего общего с «может сравнивать адреса» и а) я не вижу, как это отличается от «традиционного смысла» значения r; б) Я не понимаю, почему такая причина требует новой категории значений в языке (ну, хорошо, это позволяет обеспечить динамическую типизацию для объектов в ОО-смысле, но значения x не только относятся к объектам).
У меня лично есть еще одна ментальная модель, которая не касается непосредственно идентичности, памяти и тому подобного.
prvalue
происходит от «чистого значения», в то время как xvalue
происходит от «истекающего значения», и эта информация, которую я использую в своей ментальной модели:
Чистое значение относится к объекту, который является временным в «чистом смысле»: выражение, для которого компилятор может с абсолютной уверенностью сказать, что его оценка — это объект, который является временным, который только что был создан, и который немедленно истекает (если мы не вмешиваемся продлить его срок службы путем привязки ссылки). Объект был создан во время оценки выражения и он умрет по правилам «выражения матери».
В отличие от истекающее значение является выражением, которое оценивает ссылку на объект, который обещанный скоро истекает. То есть он дает вам обещание, что вы можете делать с этим объектом все, что захотите, потому что он все равно будет уничтожен. Но вы не знаете, когда этот объект был создан, или когда он должен быть уничтожен. Вы просто знаете, что вы «перехватили» его, поскольку он вот-вот умрет.
На практике:
struct X;
auto foo() -> X;
X x = foo();
^~~~~
в этом примере оценки foo()
приведет к prvalue
, Просто взглянув на это выражение, вы узнаете, что этот объект был создан как часть возвращения foo
и будет уничтожен в конце этого полного выражения. Поскольку вы знаете все эти вещи, вы можете прологировать это на всю жизнь:
const X& rx = foo();
теперь объект, возвращаемый foo, имеет время жизни, увеличенное до времени жизни rx
auto bar() -> X&&
X x = bar();
^~~~
В этом примере оценка bar()
приведет к xvalue
, bar
обещания вы, который дает вам объект, срок действия которого скоро истечет, но вы не знаете, когда этот объект был создан. Это может быть создано до вызова bar
(как временный или нет), а затем bar
дает вам rvalue reference
к этому. Преимущество в том, что вы знаете, что можете делать с ним все, что захотите, потому что оно не будет использоваться после слов (например, вы можете от него отказаться). Но вы не знаете, когда этот объект должен быть уничтожен. Таким образом, вы не можете продлить срок его службы — потому что вы не знаете, каково его первоначальное время жизни:
const X& rx = bar();
это не продлит жизнь.
При вызове func(T&& t)
звонящий говорит: «здесь есть т», а также «мне все равно, что вы с ним делаете». C ++ не определяет природу «здесь».
На платформе, где эталонные параметры реализованы в виде адресов, это означает, что где-то должен присутствовать объект. На этой платформе идентичность == адрес. Однако это не требование языка, а соглашение о вызовах платформы.
Платформа может реализовывать ссылки просто путем организации регистрации объектов определенным образом как в вызывающем, так и в вызываемом объектах. Здесь личность может быть «зарегистрировать edi».