Все ли временные значения в C++?
Я программирую на C++ в течение последних нескольких лет. Но есть один вопрос, который мне не удалось выяснить. Я хочу спросить, все ли временные в C++, значения?
Если нет, может ли кто-нибудь дать мне пример, где временное значение в коде является lvalue?
7 ответов
Нет.
Спецификация языка C++ никогда не дает такого простого утверждения, как тот, о котором вы спрашиваете. В стандарте языка нигде не сказано, что "все временные объекты являются значениями". Более того, сам вопрос немного ошибочен, поскольку свойство быть r-значением в языке C++ является не свойством объекта, а скорее свойством выражения (т. Е. Свойством его результата). Это на самом деле так, как это определено в спецификации языка: для различных видов выражений говорится, когда результатом является lvalue, а когда это rvalue. Помимо прочего, это фактически означает, что временный объект может быть доступен как в виде значения, так и значения в зависимости от конкретной формы выражения, используемой для доступа.
Например, результат буквального 2 + 3
выражение, очевидно, является rvalue, временным типом int
, Мы не можем применять одинарные &
к нему с одинарным &
требует lvalue в качестве своего операнда
&(2 + 3); // ERROR, lvalue required
Однако, как мы все знаем, постоянная ссылка может быть присоединена к временному объекту, как в
const int &ri = 2 + 3;
В этом случае ссылка прикрепляется к временному, продлевая срок службы последнего. Очевидно, что как только это будет сделано, мы получим доступ к тому же временному объекту, что и lvalue. ri
, так как ссылки всегда lvalues. Например, мы можем легко и легально применить одинарный &
на ссылку и получить указатель на временный
const int *pi = &ri;
с этим указателем, оставшимся совершенно действительным, пока сохраняется временный.
Другой очевидный пример доступа lvalue к временному объекту - это когда мы обращаемся к временному объекту типа класса через его this
указатель. Результат *this
является lvalue (как всегда в случае с одинарным *
применяется к указателю данных), но это не меняет того факта, что фактический объект может легко быть временным. Для данного типа класса T
выражение T()
является значением, как это явно указано в стандарте языка, но временный объект доступен через *T().get_this()
выражение (с очевидной реализацией T::get_this()
) это значение. В отличие от предыдущего примера, этот метод позволяет сразу получить неконстантное lvalue, которое ссылается на временный объект.
Итак, еще раз, тот же самый временный объект может легко "рассматриваться" как r-значение или как l-значение в зависимости от того, какое выражение (какой тип пути доступа) вы используете, чтобы "посмотреть" на этот объект.
Prasoon Saurav уже связал очень хорошую ветку clC++. Там Джеймс Канзе объясняет, почему вопрос на самом деле не имеет смысла. Это сводится к:
- rvalue-ness является (логическим) свойством выражений - каждое выражение является либо lvalue, либо rvalue
- Временные не выражения
По этой причине вопрос не имеет смысла.
Хорошим примером является следующий код:
int main() {
const int& ri = 4;
std::cout << ri << std::endl;
}
Временный int со значением 4
это не выражение. Выражение ri
это напечатано не временно. Это lvalue, и относится к временному.
Хорошо, что оператор массива возвращает ссылку, можно ли считать, что любая функция, которая возвращает ссылку, делает то же самое? все ссылки являются константными, хотя они могут быть lvalues, они изменяют то, на что ссылаются, а не саму ссылку. то же самое верно для *
оператор,
*(a temp pointer) = val;
Клянусь, я использовал какой-то компилятор, который передавал бы временные значения любой функции, которая брала ссылку,
так что вы можете пойти:
int Afunc()
{
return 5;
}
int anotherFunc(int & b)
{
b = 34;
}
anotherFunc(Afunc());
не могу найти тот, который позволяет вам сделать это сейчас, хотя ссылка должна быть константной, чтобы разрешить передачу временных значений.
int anotherFunc(const int & b);
в любом случае, ссылки могут быть lvalues и временными, трюк, являющийся ссылкой, которую он сам не изменяет, только то, на что он ссылается.
если посчитать->
Если оператор является оператором, то временные указатели могут быть l-значениями, но применяется то же условие: не временный указатель, который будет изменен, а то, на что он указывает.
Операция индексирования массива является как временной, так и l-значением, что-то вроде [10] = 1 является примером того, что вы ищете; lvalue является временным вычисляемым указателем.
Короткий ответ: да, но я не собираюсь цитировать стандарт, потому что доказательство этого вопроса потребовало бы рассмотрения всех видов временных. По определению, временный объект имеет время жизни, равное одному выражению, поэтому назначение элементов одному будет в лучшем случае плохим стилем.
Интересный ответ: Copy elision может сделать (часто делает) временный объект, идентичный объекту lvalue. Например,
MyClass blah = MyClass( 3 ); // temporary likely to be optimized out
или же
return MyClass( 3 ); // likely to directly initialize object in caller's frame
Изменить: что касается вопроса о наличии какого-либо временного объекта в этих случаях, §12.8/15 упоминает
операция копирования может быть опущена путем создания временного объекта непосредственно в цель пропущенной копии
что указывает на наличие временного объекта, который может быть идентичен lvalue.
Это зависит от того, что вы считаете временной переменной. Вы можете написать что-то вроде
#include <stdio.h>
int main()
{
char carray[10];
char *c=carray+1;
*(c+2+4) = 9;
printf("%d\n",carray[7]);
return 0;
}
Это работает в VisualStudios и GCC. Вы можете запустить код в кодовой панели
Я считаю (c+2+4) значением, хотя хочу присвоить ему. Когда я разыграю его, оно станет lvalue. Так что да, все временные являются ценностями. Но вы можете превратить rvalues (таким образом, временные) в lvalue, разыменовав его
Если нет, может ли кто-нибудь дать мне пример, где временное значение в коде является lvalue?
Следующий код привязывает постоянную ссылку к временному объекту типа const float
созданный компилятором:
int i;
const float &cfr = i;
Поведение "как будто":
int i;
const float __tmp_cfr = i; // introduced by the compiler
const float &cfr = __tmp_cfr;