Ну, как уже сказал другой ответчик, причина, по которой ++i
является lvalue, заключается в том, чтобы передать его в ссылку.
int v = 0;
int const & rcv = ++v; // would work if ++v is an rvalue too
int & rv = ++v; // would not work if ++v is an rvalue
Причина второго правила состоит в том, чтобы позволить инициализировать ссылку, используя литерал, когда ссылка является ссылкой на const:
void taking_refc(int const& v);
taking_refc(10); // valid, 10 is an rvalue though!
Почему мы вообще вводим значение, которое вы можете спросить? Итак, эти термины возникают при построении языковых правил для этих двух ситуаций:
- Мы хотим иметь значение локатора. Это будет представлять местоположение, которое содержит значение, которое можно прочитать.
- Мы хотим представить значение выражения.
Вышеупомянутые два пункта взяты из Стандарта C99, который включает эту полезную сноску, весьма полезную:
[Название val val lvalue ’’ происходит от
из выражения присваивания E1 =
Е2, в котором левый операнд Е1
должен быть (изменяемым) значением.
Возможно, это лучше рассматривать как
представляющий объект ‘‘ локатор
значение''. Что иногда называют
Val val rvalue ’’ в этом международном
Стандарт описывается как значение ‘‘
выражение''. ]
Значение локатора называется lvalue , а значение, полученное в результате оценки этого местоположения, называется rvalue . Это верно и в соответствии со стандартом C ++ (речь идет о преобразовании lvalue-в-значение):
4.1 / 2: значение, содержащееся в объекте
обозначенный lvalue является rvalue
результат.
Заключение
Используя вышеуказанную семантику, теперь ясно, почему i++
не является lvalue, а является rvalue. Поскольку возвращаемое выражение больше не находится в i
(оно увеличивается!), Оно может представлять интерес только для значения. Изменение значения, возвращаемого i++
, не имеет смысла, потому что у нас нет места, из которого мы могли бы снова прочитать это значение. Таким образом, Стандарт говорит, что это значение, и поэтому может связываться только со ссылкой на const.
Однако, в противоположность, выражение, возвращаемое ++i
, является местоположением (lvalue) i
. Провоцирование преобразования lvalue в rvalue, как в int a = ++i;
, будет считывать значение из него. В качестве альтернативы, мы можем указать на него контрольную точку и прочитать значение позже: int &a = ++i;
.
Обратите внимание также на другие случаи, когда генерируются значения r. Например, все временные значения - это rvalues, результат двоичных / унарных + и минус, а также все выражения возвращаемых значений, которые не являются ссылками. Все эти выражения не находятся в именованном объекте, а содержат только значения. Эти значения, конечно, могут быть подкреплены объектами, которые не являются постоянными.
Следующая версия C ++ будет включать в себя так называемые rvalue references
, которые, даже если они указывают на nonconst, могут связываться с rvalue. Смысл в том, чтобы иметь возможность «украсть» ресурсы у этих анонимных объектов и избежать копирования. Если предположить, что тип класса имеет перегруженный префикс ++ (возвращающий Object&
) и postfix ++ (возвращающий Object
), то сначала будет вызвана копия, а во втором случае будут украдены ресурсы из значения r:
Object o1(++a); // lvalue => can't steal. It will deep copy.
Object o2(a++); // rvalue => steal resources (like just swapping pointers)