|
|||||||
Вопросы и ответы на RSDN. Что это такое lvalue и rvalue?
Время создания: 19.11.2017 20:13
Автор: Павел Кузнецов
Текстовые метки: c++, lvalue, rvalue
Раздел: Компьютер - Программирование - Язык C++ (Си++)
Запись: xintrea/mytetra_syncro/master/base/1511111617oyqqeymoa7/text.html на raw.github.com
|
|||||||
|
|||||||
Что это такое lvalue и rvalue? Чтобы получить представление о понятиях lvalue и rvalue, проще всего рассмотреть, как выглядит некая переменная с точки зрения компилятора, так сказать "изнутри". int i = 10; С переменной i связаны две вещи: адрес и значение объекта, который она обозначает. Пусть, для определенности, адрес i — 0x1000. Значение, помещенное по этому адресу, как мы видим, — число 10. Незаметно для нас компилятор попеременно использует то одно, то другое из этих двух чисел. Например: int j; j = i; В этом месте компилятор должен сгенерировать команду (или последовательность команд), которая означает следующее: «извлечь значение переменной i (число 10, записанное по адресу 0x1000) и записать его по адресу переменной j». Или: ++i; «Нарастить то, что находится по адресу переменной i (0x1000) на 1». i = 20; «Поместить по адресу 0x1000 значение 20». В отличие от переменной i, с литералом 20 никакие адреса не связаны, только значение. В частности, выражения вида: ++20; 20 = 10; никакого смысла в С или C++ не имеют. Таким образом, с любым выражением связаны либо адрес и значение, либо только значение. Для того, чтобы отличать выражения, обозначающие объекты, от выражений, обозначающих только значения, ввели понятия lvalue и rvalue. Изначально слово lvalue использовалось для обозначения выражений, которые могли стоять слева от знака присваивания (left-value); им противопоставлялись выражения, которые могли находиться только справа от знака присваивания (right-value). Развитие языков C и C++ привело к утрате словом lvalue своего первоначального значения. Иногда lvalue трактуют также как locator value. С каждой функцией компилятор также связывает две вещи: ее адрес и ее тело («значение»). При необходимости выражение, обозначающее функцию, компилятор может привести к указателю на эту функцию. «Значениями» функций в языках C и C++ оперировать нельзя. В спецификации языка C++ термин lvalue относится также и к выражениям, обознающим функции. В стандарте языка C для выражений, обозначающих функции, используется отдельный термин — functiondesignator. Необходимо подчеркнуть, что lvalue/rvalue является свойством не объектов и/или функций, а выражений. Например: char a [10]; i — lvalue ++i — lvalue *&i — lvalue a[5] — lvalue a[i] — lvalue однако: 10 — rvalue i + 1 — rvalue i++ — rvalue modifiable lvalues, non-modifiable lvalues Не все выражения, являющиеся lvalue, могут быть использованы для модификации обозначаемых ими объектов. Например, если t определена как "const int& t = i;", выражение t, будучи lvalue, не может быть использовано для модификации объекта, который оно обозначает. Такие выражения называют non-modifiable lvalues; им противопоставляют modifiablelvalues, которые могут быть использованы для модификации обозначаемых ими объектов. Очевидно, что если мы будем использовать любое из приведенных ранее lvalue-выражений в контексте, требующем значения объекта (например, в правой части операции присваивания), использование компилятором адресов будет ошибкой. Т.е., хотя выражение i является lvalue ("адрес и значение"), компилятор должен использовать соответствующее rvalue ("значение"). В терминах стандарта C++ это называется преобразованием lvalue к rvalue (lvalue-to-rvalue conversion). В качестве критерия того, является ли некоторое выражение e lvalue, часто предлагают использовать возможность помещения этого выражения по левую сторону от знака присваивания, т.е. если выражение "e = . . ." допустимо, то e — lvalue. Однако этот критерий «не работает». Во-первых он не «пропускает» некоторые lvalues. Например, хотя выражения, состоящие только из имени массива или функции, являются lvalue, они не могут стоять по левую сторону от '='. Во-вторых он может ложно «диагностировать» некоторые rvalues как lvalues. Например, в C++ для rvalues классов можно вызывать функции члены, поэтому некоторые rvalues вполне могут находиться слева от '=': class C { }; int main() { C() = C(); // OK return 0; } Данная программа, хотя и не слишком осмысленна, вполне «законна» с точки зрения спецификации языка C++. При этом выражение С(), стоящее слева от '=', по правилам языка C++ является rvalue, Одним из более успешно «работающих» критериев может служить возможность применения к выражению операции взятия адреса, т.е. если выражение "&e" допустимо, то e — lvalue. В частности, этот критерий правильно «срабатывает» для большинства временных объектов и non-modifiable lvalues. Однако, в C++ и здесь не все гладко: если в классе C переопределена операция взятия адреса (унарная операция &) и при этом она помещена в секцию private, вне определения класса C или его «друзей» к lvalue-выражению, обозначающему объект типа C, унарная операция & будет неприменима: class C { private: C* operator &() { return this; } }; int main() { C c; &c; // ERRORreturn 0; } И наоборот: если унарная операция & определена в секции public, к выражению C(), являющемуся rvalue, можно будет применять унарную операцию &: class C { public: C* operator &() { return this; } }; int main() { &C(); // OK return 0; } В принципе, возможность применения к выражению встроенной операции получения адреса (унарная операция &) можно считать достаточным критерием того, что выражение является lvalue. При наличии сомнений следует руководствоваться спецификацией языка. Вот перечисление некоторых характерных случаев: rvalue:
lvalue:
|
|||||||
Так же в этом разделе:
|
|||||||
|
|||||||
|