Константа как rvalue в С++ (11)

Почему const int не значение R в С++ (11)? Я думал, что значение R - это "все", которое не может быть слева, а константы - это. Этот код не работает:

int f(int && x) { return 100; }

void g() {
  const int x = 1;
  f(x);
}

error: invalid initialization of reference of type ‘int&&’ from expression
of type ‘const int’

Ответ 1

Хорошо, существуют три категории выражений 1:

  • те, которые представляют объекты, которые имеют идентификатор и не могут быть перемещены из;
  • те, которые представляют объекты, которые имеют идентификатор и могут быть перемещены из;
  • те, которые представляют объекты, которые не имеют идентификатора и могут быть перемещены из;

Первые называются lvalues, а вторыми являются значения x, а третьи - prvalues. Если мы поместим lvalues ​​и xvalues ​​вместе, мы получим glvalues. Glvalues ​​- это все выражения, представляющие объекты с идентификатором. Если мы поместим xvalues ​​и prvalues ​​вместе, мы имеем rvalues. Rvalues ​​- это все выражения, представляющие объекты, которые можно перемещать.

Соответствующее выражение x является glvalue: можно написать &x, поэтому объект явно имеет тождество.

Можем ли мы перейти от этого выражения? Срок действия этого объекта истекает? Нет. Он истекает через некоторое время после текущего выражения. Это означает, что он не может быть перенесен. Это делает его lvalue.

Все эти имена могут быть немного запутанными, потому что lvalue и rvalue в С++ больше не означают, что они имели в виду в их истоках C. Значение С++ полностью не связано с левой или правой стороной назначения 2.

Лично я предпочитаю использовать терминологию этой статьи по Bjarne: iM-значения (вместо lvalues), im-values ​​(вместо xvalues), Im -значения (вместо prvalues), i-значения (вместо glvalues) и m-значения (вместо rvalues). Это не терминология, которую стандарт использует, к сожалению.


1 Здесь "имеет тождество" означает "его адрес может быть принят"; "может быть перемещен из" означает, что он скоро истечет либо из-за его временного характера, либо потому, что программист сделал это явным в системе типов, вызвав std::move или что-то подобное.

2 Вы можете иметь rvalues ​​в левой части назначения: std::vector<int>(17) = std::vector<int>(42) является допустимым выражением, даже если оно бесполезно.

Ответ 2

Я думал, что значение R было "ничего", которое не может быть слева стороны [операции назначения]

Это определение С++ 03 rvalue, и даже тогда его коллоквиализм, а не универсальный.

В С++ 11 определения для lvalue и rvalue несколько изменились. Правила сложны, и отдельные ситуации обрабатываются в каждом конкретном случае в Стандарте, но здесь общее правило:

  • Если вы можете принять адрес чего-то, это lvalue
  • Если тип выражения является ссылкой lvalue, это выражение является lvalue
  • Если ни одно из указанных выше не применяется, это rvalue

В вашем конкретном случае вы можете взять адрес x (например, у него есть имя), и поэтому оно является lvalue.

Вы можете прочитать больше о lvalues ​​и rvalues ​​в С++ 11 в двух отличных статьях:

Ответ 3

Тип параметра - это ссылка rvalue, а стандарт запрещает инициализацию  ссылки rvalue с lvalue типа "reference-related".

8.5.3 Ссылки [dcl.init.ref]

...

5... Если T1 относится к T2, а ссылка - rvalue ссылка, выражение инициализатора не должно быть lvalue.

Следовательно, следующее сообщение об ошибке:

void f (long &&);

const long x = 1;
void g () { f (x); }

Но не следующее:

void f (long &&);

const int x = 1;
void g () { f (x); }

Итак, причина ошибки: не просто "инициализация не с rvalue", а потому, что "инициализация имеет значение l ссылочного типа".

Ответ 4

В простых словах вы должны "украсть" значение rvalue. В этом и заключается всякое различие в отношении rvalues. Так что rvalue должно быть unnamed-going-to-die-in-near-future-something.