Что такое rvalues, lvalues, xvalues, glvalues ​​и prvalues? - PullRequest
1252 голосов
/ 30 августа 2010

В C ++ 03 выражением является либо значение r , либо значение .

В C ++ 11 выражение может быть:

  1. Rvalue
  2. именующий
  3. xvalue
  4. glvalue
  5. prvalue

Две категории стали пятью категориями.

  • Что это за новые категории выражений?
  • Как эти новые категории относятся к существующим категориям rvalue и lvalue?
  • Являются ли категории rvalue и lvalue в C ++ 0x такими же, как и в C ++ 03?
  • Зачем нужны эти новые категории? Неужели боги WG21 просто пытаются сбить нас с толку простых смертных?

Ответы [ 11 ]

586 голосов
/ 30 августа 2010

Полагаю, этот документ может послужить кратким введением: n3055

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

Они нужны?Вероятно, нет, если мы хотим отказаться от новых функций.Но для лучшей оптимизации нам, вероятно, следует принять их.

Цитирование n3055 :

  • lvalue (исторически так называемый, потому что lvalues ​​может появляться в левой части выражения присваивания) обозначает функцию или объект. [Пример: если E является выражением типа указателя, то *E является выражением lvalue, относящимся к объекту или функции, на которые указывает E.В качестве другого примера, результатом вызова функции, тип возвращаемой которой является ссылкой на lvalue, является lvalue.]
  • xvalue (значение «eXpiring») также относится кобъект, как правило, ближе к концу его времени жизни (так что его ресурсы могут быть перемещены, например).Xvalue - это результат некоторых видов выражений, включающих ссылки на rvalue. [Пример. Результатом вызова функции, тип возвращаемой которой является ссылкой на rvalue, является значение xvalue.]
  • A glvalue («обобщенное» lvalue) - это lvalue или xvalue .
  • rvalue (так называемый, исторически, потому что rvalue мог появиться в правой части выражения присваивания) является xvalue, временным объектом или подобъектом или значениемэто не связано с объектом.
  • A prvalue («чистое» rvalue) - это rvalue, которое не является xvalue. [Пример: результат вызова функции, тип возвращаемой информации которой не является ссылкой, является предварительным значением]

Документ, о котором идет речь, является отличной ссылкой на этот вопрос, поскольку он показываетточные изменения в стандарте, произошедшие в результате введения новой номенклатуры.

325 голосов
/ 30 августа 2010

Что это за новые категории выражений?

FCD (n3092) имеет отличное описание:

- AnЗначение lvalue (исторически так называемое, потому что значения lvalue могут появляться в левой части выражения присваивания) обозначает функцию или объект.[Пример: если E является выражением типа указателя, тогда * E является выражением lvalue, ссылающимся на объект или функцию, на которые указывает E.В качестве другого примера, результатом вызова функции, тип возвращаемой которой является ссылкой на lvalue, является lvalue.- конец примера]

- xvalue (значение «eXpiring») также относится к объекту, обычно ближе к концу его времени жизни (так, например, его ресурсы могут быть перемещены).Xvalue - это результат некоторых видов выражений, включающих ссылки на rvalue (8.3.2).[Пример: Результатом вызова функции, тип возвращаемой которой является ссылкой на rvalue, является значение xvalue.- конец примера]

- glvalue («обобщенное» lvalue) - это lvalue или xvalue.

- rvalue (так называемый, исторически, потому что rvalue может появляться справа)сторона выражений присваивания) - это xvalue, временный объект (12.2) или его подобъект или значение, которое не связано с объектом.

- prvalue («чистое» rvalue) - это rvalue, котороене является значением x[Пример: результат вызова функции, тип возвращаемой которой не является ссылкой, является предварительным значением.Значение литерала, такого как 12, 7.3e5 или true, также является prvalue.- конец примера]

Каждое выражение принадлежит ровно к одной из основных классификаций в этой таксономии: lvalue, xvalue или prvalue.Это свойство выражения называется его категорией значений.[Примечание: обсуждение каждого встроенного оператора в разделе 5 указывает категорию получаемого им значения и категории значений ожидаемых им операндов.Например, встроенные операторы присваивания ожидают, что левый операнд является lvalue, а правый операнд является prvalue и в результате выдает lvalue.Определяемые пользователем операторы являются функциями, а категории значений, которые они ожидают и получают, определяются их параметрами и типами возвращаемых данных.—Конец примечания

Предлагаю прочитать весь раздел 3.10 Lvalues ​​и rvalues ​​ хотя.

Как эти новые категории связаны с существующим rvalueа lvalue категории?

Опять же:

Taxonomy

Являются ли категории rvalue и lvalue в C ++ 0x такими же, как в C ++ 03?

Семантика значений r особенно изменилась с введением семантики перемещения.

Зачем нужны эти новые категории?

Итакэта конструкция / назначение перемещения могут быть определены и поддержаны.

173 голосов
/ 31 августа 2010

Начну с вашего последнего вопроса:

Зачем нужны эти новые категории?

Стандарт C ++ содержит много правил, относящихся к категории значений выражения.Некоторые правила проводят различие между lvalue и rvalue.Например, когда дело доходит до разрешения перегрузки.Другие правила делают различие между glvalue и prvalue.Например, у вас может быть glvalue с неполным или абстрактным типом, но не существует prvalue с неполным или абстрактным типом.До того, как у нас появилась эта терминология, правила, которые фактически должны различать glvalue / prvalue, относились к lvalue / rvalue, и они были либо непреднамеренно неправильными, либо содержали множество объяснений и исключений из правила а-ля "... если только значение rvalue не связано с неназваннымЗначение ссылки ... ».Таким образом, кажется хорошей идеей просто дать понятиям glvalues ​​и prvalues ​​их собственное имя.

Что это за новые категории выражений?Как эти новые категории связаны с существующими категориями rvalue и lvalue?

У нас все еще есть термины lvalue и rvalue, совместимые с C ++ 98.Мы просто разделили rvalues ​​на две подгруппы, xvalues ​​и prvalues, и мы называем lvalues ​​и xvalues ​​как glvalues.Xvalues ​​- это новый тип категории значений для безымянных ссылок на rvalue.Каждое выражение является одним из этих трех: lvalue, xvalue, prvalue.Диаграмма Венна будет выглядеть следующим образом:

    ______ ______
   /      X      \
  /      / \      \
 |   l  | x |  pr  |
  \      \ /      /
   \______X______/
       gl    r

Примеры с функциями:

int   prvalue();
int&  lvalue();
int&& xvalue();

Но также не забывайте, что именованные ссылки rvalue являются lvalues:

void foo(int&& t) {
  // t is initialized with an rvalue expression
  // but is actually an lvalue expression itself
}
150 голосов
/ 04 марта 2012

Зачем нужны эти новые категории? Боги WG21 просто пытаются сбить нас с толку простых смертных?

Мне не кажется, что другие ответы (хотя и хорошие, хотя многие из них) действительно отражают ответ на этот конкретный вопрос. Да, эти категории и тому подобное существуют для обеспечения семантики перемещения, но сложность существует по одной причине. Это единственное нерушимое правило перемещения вещей в C ++ 11:

Ты должен двигаться только тогда, когда это, безусловно, это безопасно.

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

В самой ранней версии ссылок на r-значения движение происходило легко. Слишком легко. Достаточно легко, что был большой потенциал для неявного перемещения вещей, когда пользователь на самом деле не хотел этого делать.

Вот обстоятельства, при которых можно что-то переместить:

  1. Когда это временный объект или его подобъект. (Prvalue)
  2. Когда пользователь явно попросил переместить его .

Если вы сделаете это:

SomeType &&Func() { ... }

SomeType &&val = Func();
SomeType otherVal{val};

Что это делает? В более старых версиях спецификации, до того, как 5 значений пришли, это спровоцировало бы движение. Конечно, это так. Вы передали ссылку rvalue в конструктор, и, таким образом, он связывается с конструктором, который принимает ссылку rvalue. Это очевидно.

Есть только одна проблема с этим; Вы не просили переместить его. О, вы можете сказать, что && должно было быть подсказкой, но это не меняет того факта, что оно нарушило правило. val не является временным, потому что временные имена не имеют. Возможно, вы продлили срок действия временного, но это означает, что он не временный ; это как любая другая переменная стека.

Если это не временно, и вы не просили его переместить, то перемещение - неправильно.

Очевидное решение - сделать val lvalue. Это означает, что вы не можете отойти от этого. Хорошо; это имя, так что это lvalue.

Как только вы это сделаете, вы уже не сможете сказать, что SomeType&& означает то же самое везде. Теперь вы сделали различие между именованными ссылками rvalue и безымянными ссылками rvalue. Ну, именованные ссылки rvalue являются lvalues; это было наше решение выше. Итак, что мы называем безымянными ссылками rvalue (возвращаемое значение из Func выше)?

Это не lvalue, потому что вы не можете выйти из lvalue. И нам нужно , чтобы иметь возможность двигаться, вернув &&; как еще вы могли бы явно сказать, чтобы переместить что-то? Вот что возвращает std::move, в конце концов. Это не r-значение (в старом стиле), потому что оно может быть в левой части уравнения (на самом деле все немного сложнее, см. этот вопрос и комментарии ниже). Это не lvalue или rvalue; это новая вещь.

У нас есть значение, которое вы можете рассматривать как lvalue, за исключением , из которого оно неявно перемещается. Мы называем это xvalue.

Обратите внимание, что xvalues ​​- это то, что заставляет нас получить другие две категории значений:

  • Prvalue - это на самом деле просто новое имя для предыдущего типа rvalue, то есть это значения, которые не являются xvalues.

  • Glvalues ​​- это объединение значений x и l в одну группу, поскольку они имеют много общих свойств.

Так что на самом деле все сводится к значениям x и необходимости ограничивать движение точно и только в определенных местах. Эти места определяются категорией rvalue; prvalues ​​- это неявные ходы, а xvalues ​​- это явные ходы (std::move возвращает xvalue).

118 голосов
/ 03 июля 2016

ИМХО, лучшее объяснение его значения дало нам Страуструп + с учетом примеров Даниеля Шандора и Мохана :

Страуструп:

Теперь я серьезно переживаю. Ясно, что мы отправились в тупик или беспорядок или оба. Я провел обед, делая анализ, чтобы увидеть, какие свойств (значений) были независимыми. Было только два независимые свойства:

  • has identity - то есть и адрес, указатель, пользователь может определить, идентичны ли две копии и т. Д.
  • can be moved from - т. Е. Нам разрешено выходить к источнику «копии» в каком-то неопределенном, но действительном состоянии

Это привело меня к выводу, что существует ровно три вида значения (используя трюк регулярного выражения с использованием заглавной буквы в указать минус - я спешил)

  • iM: имеет личность и не может быть перемещен из
  • im: имеет идентичность и может быть перемещен из (например, результат преобразования lvalue в ссылку на rvalue)
  • Im: не имеет идентификатора и может быть перемещен из.

    Четвертая возможность, IM, (не имеет идентичности и не может быть перемещена) не полезно в C++ (или, я думаю) на любом другом языке.

В дополнение к этим трем основным классификациям ценностей мы имеют два очевидных обобщения, которые соответствуют двум независимые свойства:

  • i: есть личность
  • m: можно переместить с

Это заставило меня поставить эту диаграмму на доску: enter image description here

Нейминг

Я заметил, что у нас была только ограниченная свобода имен: левые (обозначенные iM и i) - это то, что люди с более или менее формальности назвали lvalues и две точки справа (помеченные m и Im) - это то, что люди с более или менее формальностью позвонили rvalues. Это должно быть отражено в наших именах. То есть, левая «нога» W должна иметь имена, связанные с lvalue, а Правая «нога» W должна иметь имена, связанные с rvalue. Замечу что вся эта дискуссия / проблема возникают из-за введения Rvalue ссылки и переместить семантику. Эти понятия просто не существуют в мире Стрейчи, состоящем всего из rvalues и lvalues. Кто то заметил, что идеи, которые

  • Каждый value является либо lvalue, либо rvalue
  • lvalue не является rvalue, а rvalue не является lvalue

глубоко укоренились в нашем сознании, очень полезные свойства и следы этой дихотомии можно найти по всему проекту стандарта. Мы все согласились с тем, что мы должны сохранить эти свойства (и сделать их точный). Это еще больше ограничило наш выбор имен. Я заметил что стандартная формулировка библиотеки использует rvalue для обозначения m ( обобщение), чтобы сохранить ожидание и текст В стандартной библиотеке должна быть названа правая нижняя точка W rvalue.

Это привело к целенаправленному обсуждению имен. Во-первых, нам нужно было решить на lvalue. Должно ли lvalue означать iM или обобщение i? Светодиод Даг Грегор, мы перечислили места в основной формулировке языка где слово lvalue было квалифицировано для обозначения одного или другого. список был составлен и в большинстве случаев и в самом хитром / хрупком тексте lvalue в настоящее время означает iM. Это классическое значение lvalue потому что «в старые времена» ничего не двигалось; move это новое понятие в C++0x. Кроме того, имя верхней точки W lvalue дает нам свойство, что каждое значение является lvalue или rvalue, но не обоими.

Итак, верхняя левая точка W равна lvalue, а нижняя правая точка это rvalue. Что означает нижнюю левую и верхнюю правую точки? Нижний лТочка eft является обобщением классического lvalue, позволяющего двигаться.Так что это generalized lvalue. Мы назвали его glvalue. Вы можете поспорить о сокращении, но (я думаю) не с логикой.Мы предполагали, что при серьезном использовании generalized lvalue будет так или иначе сокращено, поэтому нам лучше сделать это немедленно (или рискнуть путаницей).Верхняя правая точка W является менее общей, чем нижняя правая (теперь, как всегда, она называется rvalue).Эта точка представляет собой исходное чистое понятие объекта, с которого вы можете двигаться, потому что на него нельзя ссылаться снова (кроме как деструктором).Мне понравилась фраза specialized rvalue в отличие от generalized lvalue, но pure rvalue, сокращенно до prvalue, выиграл (и, вероятно, это правильно).Итак, левая нога W - lvalue и glvalue, а правая нога - prvalue и rvalue. Кстати, каждое значение является либо glvalue, либо prvalue, но не обоими.

Это оставляет верхнюю середину W: im;то есть значения, которые имеют идентичность и могут быть перемещены.У нас действительно нет ничего, что помогло бы нам найти хорошее имя для тех эзотерических зверей.Они важны для людей, работающих с (черновым) стандартным текстом, но вряд ли станут нарицательным.Мы не нашли каких-либо реальных ограничений в именовании, чтобы направлять нас, поэтому мы выбрали «х» для центра, неизвестного, странного, только xpert или даже с рейтингом x.

Steve showing off the final product

38 голосов
/ 20 января 2016

Введение

ISOC ++ 11 (официально ISO / IEC 14882: 2011) является самой последней версией стандарта языка программирования C ++. Он содержит некоторые новые функции и концепции, например:

  • rvalue ссылки
  • категории значений выражений xvalue, glvalue, prvalue
  • семантика перемещения

Если мы хотим понять концепции новых категорий значений выражений, мы должны знать, что существуют ссылки на rvalue и lvalue. Лучше знать, что значения могут быть переданы неконстантным ссылкам значения.

int& r_i=7; // compile error
int&& rr_i=7; // OK

Мы можем получить некоторое представление о понятиях категорий значений, если процитируем подраздел «Значения и значения» из рабочего проекта N3337 (наиболее похожего на опубликованный стандарт ISOC ++ 11).

3.10 Lvalues ​​и rvalues ​​[basic.lval]

1 Выражения классифицированы в соответствии с таксономией на рисунке 1.

  • lvalue (так называемое исторически, потому что lvalue может появляться в левой части выражения присваивания) обозначает функцию или объект. [Пример: если E является выражением типа указателя, то * E является выражением lvalue, относящимся к объекту или функции, на которую указывает E. В качестве другого примера, результат вызова функции тип возвращаемого значения - ссылка lvalue - lvalue. - конец примера]
  • Значение xvalue (значение «eXpiring») также относится к объекту, обычно ближе к концу его времени жизни (так что его ресурсы могут быть перемещены, для пример). Xvalue является результатом некоторых видов выражений включая ссылки на значения (8.3.2). [Пример: результат вызова функция, тип возвращаемой которой является ссылкой на rvalue, является xvalue. -конец пример]
  • glvalue («обобщенное» lvalue) - это lvalue или xvalue.
  • rvalue (так называемое исторически, потому что rvalue может появиться в правой части выражения присваивания) - это xvalue, a
    временный объект (12.2) или его подобъект, или значение, которое не является
    связанный с объектом.
  • prvalue («чистое» rvalue) - это rvalue, которое не является xvalue. [Пример: результат вызова функции, тип возврата которой не является
    ссылка является prvalue. Значение литерала, такого как 12, 7.3e5 или
    истина также prvalue. - конец примера]

Каждое выражение принадлежит ровно одному из фундаментальных классификации в этой таксономии: lvalue, xvalue или prvalue. это Свойство выражения называется его категорией значения.

Но я не совсем уверен в том, что этого подраздела достаточно для четкого понимания концепций, потому что "обычно" не является действительно общим, "ближе к концу его срока службы" не совсем конкретно, "использование ссылок на rvalue" не является действительно ясно, и «Пример: результатом вызова функции, тип возвращаемой которой является ссылкой на rvalue, является значение xvalue». Похоже, змея кусает свой хвост.

КАТЕГОРИИ ПЕРВИЧНОЙ ЦЕННОСТИ

Каждое выражение принадлежит ровно одной категории первичных значений. Этими категориями значений являются категории lvalue, xvalue и prvalue.

lvalues ​​

Выражение E относится к категории lvalue тогда и только тогда, когда E относится к объекту, у которого УЖЕ есть идентификатор (адрес, имя или псевдоним), который делает его доступным вне E.

#include <iostream>

int i=7;

const int& f(){
    return i;
}

int main()
{
    std::cout<<&"www"<<std::endl; // The expression "www" in this row is an lvalue expression, because string literals are arrays and every array has an address.  

    i; // The expression i in this row is an lvalue expression, because it refers to the same entity ...
    i; // ... as the entity the expression i in this row refers to.

    int* p_i=new int(7);
    *p_i; // The expression *p_i in this row is an lvalue expression, because it refers to the same entity ...
    *p_i; // ... as the entity the expression *p_i in this row refers to.

    const int& r_I=7;
    r_I; // The expression r_I in this row is an lvalue expression, because it refers to the same entity ...
    r_I; // ... as the entity the expression r_I in this row refers to.

    f(); // The expression f() in this row is an lvalue expression, because it refers to the same entity ...
    i; // ... as the entity the expression f() in this row refers to.

    return 0;
}

xvalues ​​

Выражение E относится к категории xvalue тогда и только тогда, когда оно равно

- результат вызова функции, явной или неявной, тип возвращаемого значения которой является rvalue ссылкой на тип возвращаемого объекта, или

int&& f(){
    return 3;
}

int main()
{
    f(); // The expression f() belongs to the xvalue category, because f() return type is an rvalue reference to object type.

    return 0;
}

- приведение к rvalue-ссылке на тип объекта или

int main()
{
    static_cast<int&&>(7); // The expression static_cast<int&&>(7) belongs to the xvalue category, because it is a cast to an rvalue reference to object type.
    std::move(7); // std::move(7) is equivalent to static_cast<int&&>(7).

    return 0;
}

- классвыражение доступа к элементу, обозначающее нестатический член данных не ссылочного типа, в котором выражение объекта является значением xvalue, или

struct As
{
    int i;
};

As&& f(){
    return As();
}

int main()
{
    f().i; // The expression f().i belongs to the xvalue category, because As::i is a non-static data member of non-reference type, and the subexpression f() belongs to the xvlaue category.

    return 0;
}

- выражение указателя на член, в которомпервый операнд - это значение xvalue, а второй операнд - указатель на элемент данных.

Обратите внимание, что эффект приведенных выше правил заключается в том, что именованные ссылки на значения rvalue обрабатываются как lvalues, а безымянные ссылки на rvalueобъекты рассматриваются как значения x;rvalue ссылки на функции обрабатываются как lvalues ​​независимо от того, названы они или нет.

#include <functional>

struct As
{
    int i;
};

As&& f(){
    return As();
}

int main()
{
    f(); // The expression f() belongs to the xvalue category, because it refers to an unnamed rvalue reference to object.
    As&& rr_a=As();
    rr_a; // The expression rr_a belongs to the lvalue category, because it refers to a named rvalue reference to object.
    std::ref(f); // The expression std::ref(f) belongs to the lvalue category, because it refers to an rvalue reference to function.

    return 0;
}

prvalues ​​

Выражение E относится к категории prvalue тогда и только тогда, когдаE не относится ни к lvalue, ни к категории xvalue.

struct As
{
    void f(){
        this; // The expression this is a prvalue expression. Note, that the expression this is not a variable.
    }
};

As f(){
    return As();
}

int main()
{
    f(); // The expression f() belongs to the prvalue category, because it belongs neither to the lvalue nor to the xvalue category.

    return 0;
}

КАТЕГОРИИ СМЕШАННЫХ ЗНАЧЕНИЙ

Есть еще две важные категории смешанных значений.Этими категориями значений являются категории rvalue и glvalue.

rvalues ​​

Выражение E принадлежит категории rvalue тогда и только тогда, когда E принадлежит категории xvalue,или к категории prvalue.

Обратите внимание, что это определение означает, что выражение E относится к категории rvalue, если и только если E относится к объекту, который не имеет какой-либо идентичности, которая делает его доступным за пределамиE YET.

glvalues ​​

Выражение E относится к категории glvalue тогда и только тогда, когда E принадлежит категории lvalue или категории xvalue.

ПРАКТИЧЕСКОЕ ПРАВИЛО

Скотт Мейер опубликовал 1131 * очень полезное эмпирическое правило, позволяющее отличить значения от l-значений.

  • Если вы можете взять адрес выражения, выражение будет lvalue.
  • Если тип выражения является ссылкой lvalue (например, T & или const T & и т. Д.), это выражение является лзначение.
  • В противном случае выражение является значением r.Концептуально (и обычно также фактически) значения соответствуют временным объектам, таким как возвращаемые из функций или созданные неявными преобразованиями типов.Большинство литеральных значений (например, 10 и 5.3) также являются rvalues.
34 голосов
/ 30 августа 2010

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

С введением их было сказано, что безымянная ссылка на rvalue оценивается как rvalue, так что разрешение перегрузки предпочло бы привязки ссылок на rvalue, что заставило бы его выбирать конструкторы перемещения вместо конструкторов копирования. Но было обнаружено, что это вызывает проблемы со всех сторон, например, с Динамические типы и с квалификациями.

Чтобы показать это, рассмотрим

int const&& f();

int main() {
  int &&i = f(); // disgusting!
}

В черновиках до xvalue это было разрешено, потому что в C ++ 03 r-значения не-классовых типов никогда не классифицируются как cv. Но предполагается, что const применяется в случае rvalue-reference, потому что здесь мы do ссылаемся на объекты (= память!), А удаление const из не-классов rvalues ​​происходит главным образом по той причине, что нет объекта вокруг.

Проблема для динамических типов имеет аналогичную природу. В C ++ 03 значения класса имеют известный динамический тип - это статический тип этого выражения. Потому что, чтобы это было по-другому, вам нужны ссылки или разыменования, которые приводят к lvalue. Это не относится к неназванным ссылкам rvalue, но они могут демонстрировать полиморфное поведение. Так что, чтобы решить это,

  • безымянные rvalue ссылки становятся xvalues ​​. Они могут быть квалифицированы и потенциально могут иметь другой динамический тип. Они, как и предполагалось, предпочитают ссылки rvalue во время перегрузки и не будут привязываться к неконстантным ссылкам lvalue.

  • То, что раньше было rvalue (литералами, объектами, созданными приведениями к ссылочным типам), теперь становится prvalue . Они имеют то же предпочтение, что и xvalues ​​во время перегрузки.

  • То, что раньше было lvalue, остается lvalue.

И две группы делаются для захвата тех, которые могут быть квалифицированы и могут иметь разные динамические типы ( glvalues ​​) и те, где перегрузка предпочитает привязку ссылки rvalue ( rvalues ​​).

25 голосов
/ 17 июня 2016

Я долго боролся с этим, пока не наткнулся на объяснение cppreference.com категорий значений .

На самом деле это довольно просто, но я обнаружил, что это часто объясняется так, что его трудно запомнить. Здесь это объясняется очень схематично. Я процитирую некоторые части страницы:

Первичные категории

Категории первичных значений соответствуют двум свойствам выражений:

  • имеет идентификатор : можно определить, относится ли выражение к той же сущности, что и другое выражение, например, сравнивая адреса объектов или идентифицируемых ими функций (полученных прямо или косвенно) ;

  • можно переместить из : конструктор перемещения, оператор присваивания перемещения или другая перегрузка функции, которая реализует семантику перемещения, может связываться с выражением.

Выражения, которые:

  • имеют идентичность и не могут быть перемещены из. Они называются lvalue выражениями ;
  • имеют идентичность и могут быть перемещены из. Они называются xvalue выражениями ;
  • не имеют идентификатора и могут быть перемещены из. Они называются prvalue выражениями ;
  • не имеют идентичности и не могут быть перемещены из не используются.

1044 * именующий * Выражение lvalue («левое значение») - это выражение, которое имеет идентификатор , и нельзя переместить из . rvalue (до C ++ 11), prvalue (начиная с C ++ 11)

Выражение prvalue («pure rvalue») - это выражение, которое не имеет идентификатора и можно переместить из .

xvalue

Выражение xvalue ("expiring value") - это выражение, которое имеет идентификатор и можно перемещать из .

glvalue

Выражение glvalue («обобщенное lvalue») - это выражение, которое является либо lvalue, либо xvalue. У него есть личность . Он может или не может быть перемещен из.

rvalue (начиная с C ++ 11)

Выражение rvalue («правильное значение») - это выражение, являющееся либо prvalue, либо xvalue. Это может быть перемещено из . Может иметь или не иметь личность.

16 голосов
/ 30 августа 2010

Как эти новые категории относятся к существующим категориям rvalue и lvalue?

Значение C ++ 03 по-прежнему является значением C ++ 11, тогда как значение C ++ 03 называется prvalue в C ++ 11.

14 голосов
/ 25 июля 2016

Одно дополнение к превосходным ответам, приведенным выше, в момент, который смутил меня даже после того, как я прочитал Страуструпа и подумал, что понял различие между значением и значением.Когда вы видите

int&& a = 3,

, очень заманчиво читать int&& как тип и делать вывод, что a является значением.Это не так:

int&& a = 3;
int&& c = a; //error: cannot bind 'int' lvalue to 'int&&'
int& b = a; //compiles

a имеет имя и ipso facto является lvalue. Не думайте о && как о части типа a;это просто что-то, что говорит вам, с чем a разрешено связываться.

Это особенно важно для аргументов типа T&& в конструкторах.Если вы напишите

Foo::Foo(T&& _t) : t{_t} {}

, вы скопируете _t в t.Вам нужно

Foo::Foo(T&& _t) : t{std::move(_t)} {}, если вы хотите двигаться.Будет ли мой компилятор предупреждать меня, когда я пропущу move!

...