Подтвердить что ты не робот

Часто используются редко определенные термины: lvalue

Что такое lvalue?

4b9b3361

Ответ 1

Значение lvalue - это значение, которое может быть присвоено:

lvalue = rvalue;

Это сокращение от "left value" или "lefthand value", и в основном это значение слева от знака =, т.е. значение, которое вы присваиваете.

В качестве примера того, что не является lvalue (только для rvalue):

printf("Hello, world!\n") = 100; // WTF?

Этот код не работает, потому что printf() (функция, возвращающая int) не может быть lvalue, только rvalue.

Ответ 2

Что-то, что появляется в левой части задания, то есть что-то, что может быть назначено.

Обратите внимание, что в С++ вызов функции может быть lvalue, если:

int & func() {
   static int a = 0;
   return a;
}

то

func() = 42;     // is legal (and not uncommon)

Ответ 3

Это традиционно левая часть оператора "=". Однако со временем значение "lvalue" / "rvalue" изменилось. С++ добавил термин "немодифицируемое значение lvalue", которое является любым значением l, которое не может быть присвоено: массивы и переменные, которые имеют квалификацию "const", являются двумя примерами. В C вы не можете назначить какое-либо значение r (см. Ниже). Аналогично, в С++ вы не можете назначать значения r, которые не относятся к определенному типу определенного пользователем типа.

Вы можете сказать, что "lvalue" - это выражение, которое называет объект, который сохраняется со временем и занимает некоторое место хранения. Независимо от того, можете ли вы назначить это выражение, это не важно для этой классификации. Ссылка, в частности, также является значением lvalue, поскольку оно имеет имя, которое сохраняется со временем. Все следующие значения lvalues, потому что все они относятся к именованным объектам. Также обратите внимание, что a const не влияет на lvalue-ness.

int a; lvalue: a;
       lvalue: ++a;
int a[2]; lvalue: a;
int &ra = a; lvalue: ra;
int *pa = &a; lvalue: *pa;

Термин "rvalue" используется для таких вещей, как литералы и значения счетчика, а также для временных, которые не любят веселиться от долгой жизни и сразу же уничтожаются в конце полного выражения. Для rvalues ​​важно не аспект упорства, а значение-аспект. Функции на С++ - это lvalues, потому что они постоянны и имеют адрес, даже если они не являются объектами. Я оставил их в приведенном выше обзоре lvalues, потому что легче понять lvalues, когда сначала учитывают только объекты. Все следующие значения:

enum { FOO, BAR }; rvalue: BAR;
int a[2]; rvalue: (a+1);
rvalue: 42;
int a; rvalue: a++; // refering to a temporary
struct mystruct { }; mystruct f() { return mystruct(); } rvalue: f();

Кстати, часто у вас есть lvalue, но оператору требуется rvalue. Например, двоичный встроенный оператор "+" добавляет два значения. Выражение lvalue сначала и для всех указывает местоположение, в котором сначала нужно считывать значение. Поэтому, когда вы добавляете две переменные, происходит преобразование "lvalue to rvalue". В стандарте говорится, что значение, содержащееся в выражении lvalue, является его результатом rvalue:

int a = 0, b = 1;
int c = a + b; // read values out of the lvalues of a and b. 

Другие операторы не принимают значение r, но lvalues. Они не читают значение. Примером может служить адрес-оператор, &. Вы не можете использовать адрес выражения rvalue. Некоторые значения не являются даже объектами: они не занимают никакого хранилища. Примеры опять же - литералы (10, 3.3,...) и значения счетчика.

Как этот страшный материал полезен?

Ну, у него есть несколько преимуществ, чтобы различать lvalue и rvalue

  • Предоставление компилятору возможности опускать память для значений r и использовать регистры/память для чтения для скалярных значений
  • Флагирование выражений как неуловимое: rvalues ​​не будет долго жить
    • Позволяет эффективную семантику копирования для компилятора внутри и в С++ 1x также доступна для программиста (см. раздел "Семантика перевода и ссылки на rvalue" ): мы можем украсть ресурсы из rvalues, которые будут уничтожены в любом случае.
  • Позволяет создавать правила для этого свойства
    • rvalues ​​не могут быть сгенерированы из еще неинициализированных объектов, на которые ссылаются lvalues. Но lvalues ​​могут ссылаться на неинициализированные объекты просто отлично
    • rvalues ​​никогда не могут быть полиморфными. Их статический тип также должен быть их динамическим типом: упрощает правила для оператора typeid.

... Есть еще что-то, я чувствую это...

Ответ 4

Одно из лучших объяснений, которые я знаю, можно найти в в этой статье о ссылках на RValue.

Другой способ определить, является ли выражение lvalue, нужно спросить "могу ли я взять его адрес?". Если вы можете, то это lvalue. Если вы не можете, это rvalue. Например, & obj, & * ptr, & ptr [index] и & ++ x являются действительными (хотя некоторые из этих выражений глупы), в то время как 1729, & (x + y), < w990 > ( "мяу" ) и & x ++ - все недействительны. Почему это работает? Оператор address-of operator требует, чтобы его "операнд должен быть lvalue" (С++ 03 5.3.1/2). Зачем это требуется? Принимать адрес постоянного объекта в порядке, но обращение с временным адресом было бы чрезвычайно опасным, потому что временные ресурсы быстро испаряются.

Ответ 5

"L" в lvalue обычно описывается как "местоположение". Lvalue указывает местоположение чего-то; как указал другой ответчик, lvalues ​​обычно имеют свой адрес. Вот почему возвращаемые значения числовых литералов и не ссылочных значений не являются значениями.

L использовался для обозначения "left", пока const не был введен в С++. Значения констант не могут появляться в левой части задания.

Ответ 6

Для языка программирования C C11 проект n1570 6.3.2.1p1 делает это кратко:

Lvalue является выражением (с типом объекта, отличным от void), который потенциально обозначает объект [...]

Это так просто.

Википедия определение на день написания этого ответа было почти так же хорошо

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


Примеры выражений lvalue? Дано

int a, b[1], *c;

мы можем иметь следующие выражения lvalue: a, b и c каждый из которых определенно обозначает объект. b[0] обозначает объект, следовательно, это выражение lvalue. А как насчет потенциальности? *c всегда является выражением lvalue, но не указывает объект, если c не содержит указатель на действительный объект. b[1] может семантически назначать объект, но это не потому, что он обращается к массиву за пределами границ. Оба они являются выражениями lvalue.

Когда вычисляется выражение lvalue, которое фактически не обозначает объект, поведение не определено.

Выражение lvalue может быть более сложным, чем, например:

((flag ? foo() : bar())->pointed_to_by_struct_member[42])[0] 

если он компилируется, это выражение lvalue.

В принципе, все, что вы можете применить & (адрес-оператора) к C, является lvalue, кроме функций, поскольку функции не являются объектами C.

Итак, что же тогда означает L?

C11 n1570 Сноска 64:

64) Имя lvalue происходит первоначально из выражения присваивания E1 = E2, в котором левый операнд E1 должен быть (изменяемым) lvalue. Это, пожалуй, лучше рассматривать как представление значения локатора объекта. [...]


Обратите внимание, что E1 = E2 не требуется компилировать для E1 как lvalue в ISO C. 6.3.2.1p1 продолжается:

Модифицируемое значение lvalue является значением lvalue, которое не имеет типа массива, не имеет неполного типа, не имеет типа const -qualified, и если оно является структурой или объединением, не имеет какого-либо члена (в том числе, рекурсивно, любого член или элемент всех содержащихся агрегатов или объединений) с типом const -qualified.

Раньше это было на языках до C (например, B), что все значения локатора могли появиться в левой части задания, следовательно, именование. Это больше не относится к C, поскольку массивы никогда не могли быть назначены, а ISO C - const.


PS В той же сноске объясняется связанный термин rvalue следующим образом:

[...] То, что иногда называют rvalue, в этом международном стандарте описывается как значение выражения. [...]

Ответ 7

Из этой статьи . Поскольку ОП был немного ленив, задавая свой вопрос (хотя некоторые люди не согласны, см. Комментарии), я тоже буду ленив и просто вставляю всю соответствующую часть здесь, возможно, нарушая некоторые законы об авторском праве.

Объект - это область хранения, которая могут быть исследованы и сохранены. lvalue - выражение, которое относится к такой объект. Значение lvalue не обязательно разрешить модификацию объект, который он обозначает. Например, Объект const - это значение lvalue, которое не может быть изменен. Термин модифицируемый lvalue используется, чтобы подчеркнуть, что lvalue позволяет указанному объекту быть изменен, а также рассмотрен. следующие типы объектов - lvalues, но не изменяемые значения l

  • Тип массива
  • Неполный тип
  • Конкретно определенный тип
  • Объектом является структура или тип объединения, и один из его членов имеет const-квалифицированный тип

Потому что эти lvalues ​​не Модифицируемые, они не могут появляться на слева от оператора присваивания.

В С++ вызов функции, который возвращает Ссылка - это значение lvalue. В противном случае вызов функции - выражение rvalue. В С++ каждое выражение вырабатывает lvalue, rvalue или no value.

Некоторые операторы требуют lvalues ​​для некоторые из их операндов. Стол ниже перечислены эти операторы и дополнительные ограничения на их использование.

     Operator                          Requirement
     & (unary)                         Operand must be an lvalue.
     ++ --                             Operand must be an lvalue.
                                          This applies to both prefix
                                            and postfix forms.
     = += -= *= %= >= &= ^= |=         Left operand must be an lvalue.

Например, все операторы присваивания оценить их правый операнд и назначьте это значение слева операнд. Левый операнд должен быть Модифицируемое значение lvalue или ссылка на изменяемый объект.

Оператор адресов (&) требует lvalue как операнд, в то время как increment (++) и декремент (-) операторам требуется модифицируемое значение lvalue в качестве операнда.

Ответ 8

Простой пример того, что определенно не lvalue:

3 = 42;