Это традиционно левая часть оператора "=". Однако со временем значение «lvalue» / «rvalue» изменилось. В C ++ добавлен термин «немодифицируемое значение lvalue», которое представляет собой любое значение lvalue, которое не может быть присвоено: массивы и переменные, которые квалифицированы как «const», являются двумя примерами. В C вы не можете присвоить никакое значение (см. Ниже). Аналогично, в C ++ вы не можете присваивать значения, которые не принадлежат к определенному пользователем типу класса.
Можно сказать, что «lvalue» - это выражение, которое именует объект, который сохраняется со временем и занимает некоторое место в хранилище. Независимо от того, можете ли вы присвоить это выражение, не важно для этой классификации. Ссылка, в частности, также является lvalue, потому что она имеет имя, которое сохраняется с течением времени. Все последующие являются lvalues, потому что все они ссылаются на именованные объекты. Также обратите внимание, что 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» используется для таких вещей, как литералы и значения перечислителей, а также для временных, которые не получают удовольствия от долгой жизни и сразу же уничтожаются в конце полного выражения. Для значений важен не аспект настойчивости, а аспект ценности. Функции в C ++ являются 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 first и for all указывает место, где значение должно быть сначала считано. Поэтому, когда вы добавляете две переменные, происходит преобразование «lvalue в rvalue». Стандарт говорит, что значение, содержащееся в выражении lvalue, является его результатом rvalue:
int a = 0, b = 1;
int c = a + b; // read values out of the lvalues of a and b.
Другие операторы принимают не значения, а значения. Они не читают значение. Примером является адрес оператора &
. Вы не можете взять адрес rvalue выражений. Некоторые значения даже не являются объектами: они не занимают места в хранилище. Примерами снова являются литералы (10, 3.3, ...) и значения перечислителя.
Чем полезны эти страшные вещи?
Ну, у этого есть несколько преимуществ, чтобы иметь различие между lvalue и rvalue
- Разрешение компилятору исключать использование памяти для значений r и использование регистров / только для чтения для скалярных значений
- Пометка выражений как неуловимых: rvalues не проживет долго
- Разрешает эффективную семантику копирования для компилятора внутри и в c ++ 1x, также предоставляемую программисту (см. Семантику перемещения и ссылки на rvalue): мы можем украсть ресурсы из значений r, которые в любом случае будут уничтожены.
- Позволяет строить правила на этом свойстве
- Не допускается создание значений r из еще неинициализированных объектов, на которые ссылается значение l. Но lvalues может относиться к неинициализированным объектам просто отлично
- значения не могут быть полиморфными. Их статический тип также должен быть их динамическим типом: упрощает правила для оператора typeid.
... Это нечто большее, я чувствую это ...