Перейти к содержимому

Lvalue rvalue c что это

  • автор:

Что такое rvalue и lvalue?

На разных ресурсах нашел разные определения rvalue , lvalue . Как же правильно? right value или read value ? left value или locator value ?

Отслеживать
28.5k 12 12 золотых знаков 58 58 серебряных знаков 118 118 бронзовых знаков
задан 2 авг 2018 в 10:03
2,904 19 19 серебряных знаков 44 44 бронзовых знака
Правильно left и right
2 авг 2018 в 10:22
Никак не правильно, это не расшифровывается. Но произошло от right и left.
2 авг 2018 в 10:26

На мой взгляд, правильный первый вариант, так как взять к примеру простое выражение: a = b + c ,где (a = lvalue), a (b + c = rvalue). Судя по примеру lvalue подразумевает как бы left, но никак не read, тоже самое можно сказать и про rvalue.

2 авг 2018 в 11:22

О чем вопрос вообще? Почему в заголовке речь идет о «rvalue and lvalue references«, а в теле вопроса ни слова про references нет и идет речь просто о rvalue и lvalue? В качестве «принятого» выбран ответ, который к «rvalue and lvalue references» вообще не имеет никакого отношения. Что за бессмысленная каша?

2 авг 2018 в 14:32

@Dima Khodan: Нет. Основным критерием качества вопросов (и ответов) на SO являются их полезность для последующих читателей. Если заголовок вопроса никак не соответствует его содержанию, то ценность вопроса — «ниже плинтуса» и он подлежит правке или удалению. Ваше «тут все прекрасно поняли суть» никакого значения не имеет. Я еще раз повторю: почему в заголовке речь идет о «references«? Если ваш вопрос не имеет никакого отношения к «references», то — исправляйте заголовок.

2 авг 2018 в 21:39

2 ответа 2

Сортировка: Сброс на вариант по умолчанию

Исходно речь шла про правую и левую части относительно оператора присваивания. Но правильного варианта такой простой расшифровки нет и быть не может. Термины так и останутся rvalue и lvalue . А вот какой в них вложен смысл чётко прописано в стандарте. Всё это образует категории выражений.

иерархия выражений

  • glvalue (“generalized” lvalue) Выражение, чьё вычисление определяет сущность объекта, битового поля или функции.
  • prvalue (“pure” rvalue) Выражение, чьё вычисление инициализирует объект, битовое поле или вычисляет значение операнда оператора, с соответствии с контекстом использования. Например, литералы 42 , true , nullptr за исключением строковых литералов, которые являются lvalue выражениями.
  • xvalue (“eXpiring” lvalue) Это glvalue, которое обозначает объект или битовое поле, чьи ресурсы могут быть повторно использованы (обычно потому, что они находятся около конца своего времени жизни). Например, результат вызова std::move даёт выражение xvalue.
  • lvalue Это glvalue, которое не является xvalue. Например, имя переменной, функции или члена-данных, независимо от их типа, даже переменная, имеющая тип rvalue-ссылки, образует выражение lvalue.
  • rvalue Это prvalue или xvalue.

Таким образом любое выражение есть в первую очередь lvalue, xvalue или prvalue. rvalue — это уже обобщение.

Отслеживать
ответ дан 2 авг 2018 в 10:27
28.5k 12 12 золотых знаков 58 58 серебряных знаков 118 118 бронзовых знаков
Только вопрос такой. Что значит неполный тип?
2 авг 2018 в 12:04

@DimaKhodan A class that has been declared but not defined, an enumeration type in certain contexts (9.6), or an array of unknown bound or of incomplete element type, is an incompletely-defined object type. Incompletely-defined object types and cv void are incomplete types (6.7.1). Objects shall not be defined to have an incomplete type.

2 авг 2018 в 12:48

Не знаю, что Вы имеете ввиду под вопросом «как правильно». rvalue и lvalue — это категории выражений. Вот что написано в стандарте:

— A glvalue is an expression whose evaluation determines the identity of an object, bit-field, or function.
— A prvalue is an expression > whose evaluation initializes an object or a bit-field, or computes the value of the operand of an operator, as specified by the context in which it appears.
— An xvalue is a glvalue that denotes an object or bit-field whose resources can be reused (usually because it is near the end of its lifetime).
— An lvalue is a glvalue that is not an xvalue.
— An rvalue is a prvalue or an xvalue.

//.
[Note: Historically, lvalues and rvalues were so-called because they could appear on the left- and right-hand side of an assignment (although this is no longer generally true); glvalues are “generalized” lvalues, prvalues are “pure” rvalues, and xvalues are “eXpiring” lvalues. Despite their names, these terms classify expressions, not values. — end note]

Семантика перемещения

В С++ используемые значения мы можем разделить на две группы: lvalue и rvalue . lvalue представляет именованное значение, например, переменные, параметры, константы. С lvalue ассоциирован некоторый адрес в памяти, в котором на постоянной основе хранится некоторое значение. И мы можем lvalue присвоить некоторое значение. А rvalue — это то, что можно только присваивать, например, литералы или результаты выражений. Например:

int n = 5;

Здесь n представляет lvalue, а число 5 — rvalue. Подобные названия приняты, потому что n расположен слева от оператора присваивания (left value), а присваиваемое значение — число 5 справа от оператора присвоения (right value). Другой пример:

int n int k;

Здесь n и k — lvalue, а 5 и выражение n + 7 — rvalue.

rvalue-ссылка

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

Для установки ссылки rvalue применяются два амперсанда после имени типа:

#include int main() < int n ; int&& tempRef ; // ссылка rvalue std::cout 

В данном случае результат выражения n+3 сохраняется в памяти (в стеке), а ссылка tempRef будет представлять ссылку на это временное значение. Когда завершится функция main, соответственно завершится и область видимости переменной tempRef и будет удалено временное значение, на которое эта переменная ссылается. Стоит отметить, что tempRef, хоть и хранит ссылку на rvalue, само по себе также является lvalue.

Функция std::move

Стоит отметить, что мы не можем установить ссылку rvalue на значение lvalue, например:

int n ; int&& tempRef = n; // ! Так нельзя

Здесь n - lvalue, а ссылке rvalue мы можем только передать значение rvalue. Тем не менее в некоторых ситуациях может возникать необходимость преобразовать lvalue в rvalue. Для этого применяется встроенная функция std::move() , которая имеется по умолчанию в стандартной библиотеке C++:

#include int main() < int n ; int&& tempRef = std::move(n); // преобразуем int в int&& std::cout 

Здесь значение переменной n преобразуется из типа int в тип int&& - rvalue-ссылку на int. В данном случае практического смысла в подобном преобразовании мало, но далее на примере конструктора перемещения мы посмотрим реальную пользу подобной функции.

Также стоит обратить внимание, что при преобразовании константного значения результатом является константная ссылка:

const int m; const int&& mRef = std::move(m); // результат - константная ссылка

rvalue-ссылка как параметр функции

rvalue-ссылка может выступать в качестве параметра функции.

#include void print(std::string&& text) < std::cout int main()

Чтобы указать, что параметр представляет rvalue-ссылку, после типа указываются два амперсанда &&. То есть здесь функция print принимает rvalue-ссылку на значение std::string. При вызове этой функции ей можно передать rvalue:

print("hello");

Но нельзя передать lvalue, поэтому следующие строки НЕ скомпилируются:

std::string message = "hi world"; print(message); // ! Ошибка - передаем lvalue

Однако мы могли бы применить опять же функцию std::move() и преобразовать переменную в rvalue:

print(std::move(message));

Возвращение rvalue из функции

При возвращении значения локальной переменной или параметра компилятор рассматривает значение как rvalue. Но если возвращаемое значение представляет переменную компилятор также может выполнять оптимизацию NRVO (named return value optimization):

#include void print(std::string&& text) < std::cout std::string defaultMessage() < std::string message; return message; > int main() < print(defaultMessage()); // передаем rvalue >

Здесь функция defaultMessage возвращает rvalue, соответственно результат этой функции мы можем передать внутрь функции print. Применяемая оптимизация NRVO означает, что компилятор сохраняет объект результата непосредственно в памяти, предназначенной для хранения возвращаемого функцией значения. То есть после применения NRVO больше не выделяется память для отдельной автоматической переменной с именем message. То есть при выполнении этой программы создается только один объект std::string .

Аналогично происходит при сохранении во внешнюю переменную:

#include std::string defaultMessage() < std::string message; return message; > int main()

Здесь также создается только один объект std::string.

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

Rvalue и lvalue в C++

В одной из предыдущих статей мы рассматривали move-семантику в C++. Продолжим тему и поговорим про rvalue и lvalue.

Главное отличие rvalue и lvalue заключается в том, что объекты rvalue можно переместить, в то время как объекты lvalue — лишь скопировать. Рассматривать это лучше на практических примерах:

Screenshot_1-1801-a6ecf9.png

Данный код будет функционировать не так, как от него ждут. К примеру, std::move() всё ещё конвертирует lvalue в rvalue, однако конвертация сохраняет все модификаторы, включая const. Потом компилятор выбирает между 2-х конструкторов класса String наиболее подходящий. Так как компилятор не может отправить const rlvaue туда, где ожидается non-const rvalue, компилятор выбирает конструктор копирования, а const rvalue конвертируется обратно в const lvalue.

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

Теперь давайте перейдем ко второму примеру. Он демонстрирует правило, согласно которому результат функции и аргументы могут быть как lvalue, так и rvalue, но параметры функций при этом могут быть лишь lvalue. Под аргументом в данном случае подразумевается то, что передаётся в нашу функцию. Аргумент инициализирует параметр, доступный непосредственно внутри функции.

Screenshot_2-1801-eb5849.png

И хотя параметр string функции f() и имеет тип rvalue-ссылки, все же он является lvalue, то есть требует явной конвертации в rvalue непосредственно перед передачей в функцию g() . А вот как раз этим и занимается std::move() .

Lvalue rvalue c что это

C и C++ проводят в жизнь тонкие различия по выражениям справа и слева от знака оператора присваивания (=, assignment operator). Здесь и далее перевод колонок "Lvalues and Rvalues", "Non-modifiable Lvalues", автор Dan Saks, Embedded Systems Programming [1].

Если Вы некоторое время программировали на языке C или на языке C++, то вероятно слышали о терминах lvalue (читается как "EL-value") и rvalue (читается как "AR-value"), потому что они иногда появляются в сообщениях об ошибках компилятора. Есть также некий шанс, что у Вас нет определенного понимания, что же это все-таки означает.

Большинство книг по C или C++ объясняют lvalues и rvalues не очень хорошо (я просмотрел дюжину книг, и не мог найти ни одно понравившееся мне объяснение). Причина может быть в том, что нет последовательного определения lvalue и rvalue даже среди языковых стандартов. Спецификация 1999 C Standard определяет lvalue не так, как спецификация 1989 C Standard, и каждая из них отличается от C++ Standard. Причем ни один из стандартов не дает четкого определения.

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

lvalue-rvalue

Как часто бывает с малопонятными концепциями языка, Вы можете задать себе вопрос - зачем нужно заботиться о знании смысла lvalue и rvalue? По общему мнению, если Вы пишете только на C, то можете работать дальше, не понимая, что по сути такое lvalues и rvalues. Многие программисты так и поступают. Но понимание lvalues и rvalues предоставляет ценную способность проникновения в суть поведения встроенных операторов, и кода, который генерирует компилятор для выполнения этих операторов. Если Вы программируете на C++, то понимание работы встроенных операторов дает обязательную основу для качественного написания перезагружаемых операторов (overloaded operators).

[Базовые концепции]

Керниган и Ричи ввели термин lvalue, чтобы отделить одни выражения от других. В своей книге "C Programming Language" (Prentice-Hall, 1988) они написали: "Объектом является манипулируемая область хранения; lvalue является выражением, ссылающимся на объект. имя 'lvalue' произошло из выражения присваивания E1 = E2, в котором левый операнд E1 должен быть выражением типа lvalue."

The-C-Programming-Language

Другими словами, левые и правые операнды в выражении присваивания являются самостоятельными выражениями. Для того, чтобы присваивание было допустимым, левый операнд должен ссылаться на объект, который должен быть типа lvalue. Правый операнд может быть любым выражением, он не обязательно должен обладать свойствами lvalue. Например:

int n;

декларирует n как объект, имеющий тип int. Когда Вы используете n в выражении присваивания, типа:

n = 3;

n является выражением (точнее подвыражением выражения присваивания), ссылающимся на объект int. Выражение n является lvalue.

Предположим, что Вы переставили местами левый и правый операторы:

3 = n;

Если Вы не бывший программист языка Фортран, то очевидно это самая глупая вещь, которую можно сделать. Присвоение типа 3 = n пытается изменить значение целочисленной константы. К счастью, компиляторы C и C++ не допустят этого и выдадут ошибку. (Прим. переводчика: поскольку я иногда допускаю ошибку, когда нечаянно пишу вместо == знак присваивания =, то специально в операторе проверки == ставлю слева константу, чтобы компилятор сообщил об ошибке присваивания. Если же слева будет lvalue, то компилятор не заметит подвоха, и не сообщит об ошибке.) Основание отклонения такой операции - то, что левый операнд 3 в выражении не является lvalue. Он является rvalue и не ссылается на объект; он просто представляет собой некое значение.

Не знаю, откуда произошел термин rvalue. Ни один из стандартов C не использует его, кроме как в сноске, где указано, что "иногда стандарт описывает rvalue как 'значение выражения'".

Спецификация C++ Standard использует термин rvalue, косвенно его определяя в следующем высказывании: "Каждое выражение относится либо к lvalue, либо к rvalue.". Таким образом, rvalue - любое выражение, которое не является lvalue.

Цифровые литералы, такие как 3 и 3.14159, являются rvalue. К rvalue относятся и символьные литералы, такие как 'a'. Идентификатор, который относится к объекту, является lvalue, но идентификатор, именующий перечисляемую константу (enumeration constant), является rvalue. Например:

enum color  red, green, blue >; color c; . c = green; // ok blue = green; // ошибка

Второе присваивание вызовет ошибку при компиляции, потому что blue является rvalue.

Хотя Вы не можете использовать rvalue в качестве lvalue, Вы можете использовать lvalue в качестве rvalue. Например, определите:

int m, n;

Вы можете присвоить значение n объекту, который обозначен через m:

m = n;

Это выражение использует lvalue-выражение n в качестве rvalue. Строго говоря, компилятор выполняет то, что стандарт C++ Standard называет преобразование от lvalue к rvalue (lvalue-to-rvalue conversion), чтобы получить значение, сохраненное в объекте, который обозначен через n.

[Lvalue в других выражениях]

Хотя lvalue и rvalue получили свои имена по своим ролям (позиции) в выражениях присваивания, концепция этих понятий применяется во всех выражениях, даже в тех, которые вовлекают другие встроенные операторы.

Например, оба операнда встроенного двоичного оператора + должны быть выражениями. Очевидно, у этих выражений должны быть подходящие типы. После преобразований оба выражения должны иметь одинаковый арифметический тип, или одно выражение должно иметь тип указателя, и другое должно иметь целый тип. Но любое из них может быть или lvalue или rvalue. таким образом, оба выражения x + 2 и 2 + x являются допустимыми.

Хотя операнды бинарного оператора + могут быть lvalue, результат у него всегда rvalue. Например, даны целые объекты m и n, и следующее выражение приведет к ошибке:

m + 1 = n; //это выражение даст ошибку при компиляции

Оператор + имеет более высокий приоритет, чем оператор =. Таким образом, выражение присваивания эквивалентно следующему:

(m + 1) = n; //это выражение даст ошибку при компиляции

Ошибка происходит потому, что m + 1 является rvalue.

Как другой пример, унарный (одноместный) оператор & (взятие адреса) требует lvalue в качестве операнда. Поэтому &n является допустимым выражением только если n является lvalue. Таким образом, выражение типа &3 приведет к ошибке. Число 3 не ссылается на объект, так что адрес его получить нельзя.

Хотя унар & требует lvalue в качестве операнда, результат его будет rvalue. Пример:

int n, *p; . p = &n; // ok &n = p; // ошибка: &n является rvalue

В отличие от унара &, унар * в качестве результата выдает lvalue. Ненулевой указатель p всегда указывает на объект, поэтому *p является lvalue. Пример:

int a[N]; int *p = a; . *p = 3; // ok

Хотя результат унара * является lvalue, его операнд может быть rvalue, как тут:

*(p + 1) = 4; // ok

[Хранилище данных для rvalue]

По базовой концепции rvalue является просто значением, оно не ссылается на объект. На практике rvalue может ссылаться на объект. Просто необязательно, что rvalue ссылается на объект. Поэтому и C, и C++ настаивают, чтобы Вы программировали, как будто rvalue не ссылаются на объекты.

Предположение, что rvalue не ссылается на объект, дает компиляторам C и C++ значительную свободу в генерации кода для выражений rvalue. Предположим, что есть присваивание такого вида:

n = 1;

Здесь n является целым (int). Компилятор может сгенерировать именованное хранилище данных, инициализированное значением 1, как будто бы 1 было lvalue. Это сгенерировало бы код для копирования из инициализированного хранилища в место хранения, выделенного для n. На языке ассемблера это могло бы выглядеть так:

one: .word 1 . mov (one), n

Многие процессоры предоставляют инструкции (команды ассемблера) для непосредственной адресации операнда (immediate operand addressing), в котором операнд источника может быть частью инструкции, а не отдельными данными. На языке ассемблера это может выглядеть так:

mov #1, n

В этом случае rvalue 1 никогда не появляется как объект в пространстве данных. Скорее он появляется как инструкции в пространстве кода.

На некоторых процессорах самый быстрый способ поместить значение 1 в объект - очистить его и затем инкрементировать, например так:

clr n inc n

Очистка объекта устанавливает его содержимое в 0. Инкрементирование дает 1. И все же данные, представляющие величины 0 и 1, нигде не появляются в коде.

Хотя верно, что rvalue на языке C не ссылаются на объекты, для языка C++ это не так. В C++ rvalue, имеющие тип класса, ссылаются на объекты, но они все еще не lvalue. Таким образом все, что я уже сказал для rvalue верно, пока мы не имеем дело с rvalue типа класса.

[Немодифицируемые lvalue]

Хотя lvalue обозначают объекты, не все lvalue могут появляться как левая часть оператора присваивания. Как известно, выражение является последовательностью операторов и операндов, которые задают некие вычисления. Вычисления могут давать в результате значение, или производить сторонние эффекты (side effects). Выражение присваивание имеет форму

где e1 и e2 сами по себе выражения. Правый операнд e2 может быть любым выражением, однако левый операнд должен быть выражением lvalue, он должен ссылаться на объект (об этом говорилось ранее).

Хотя lvalue получило свое имя от вида выражения, которое должно появиться в левой части оператора присваивания, Керниган и Ричи определили его не от этого. В первой редакции книги "C Programming Language" (Prentice-Hall, 1978) они определили lvalue как выражение, ссылающееся на объект.". В тот момент времени набор выражений, относящийся к объектам, был точно тем же самым набором выражений, которые имели право появляться в левой части оператора присваивания. Но это было до того, как квалификатор const стал частью C и C++.

Ключевое слово const делает базовое понятие lvalue неадекватным семантике выражений. Мы должны быть в состоянии отличить друг от друга разные виды lvalue.

Как уже упоминалось, оператор присваивания = является не единственным, который требует lvalue в качестве операнда. Унарный оператор взятия адреса & также требует lvalue как собственного операнда. Мало того, что каждый операнд является или lvalue или rvalue, но и каждый оператор дает в результате или lvalue, или rvalue. Принимая все это во внимание, посмотрим, как квалификатор const усложняет понятие lvalue.

Квалификатор const появляется в декларации, и модифицирует тип в декларации, или некоторую часть её, например:

int const n = 127;

Здесь декларируется объект типа "const int" (постоянное целое). Выражение n ссылается на объект, как будто там нет константы, за исключением того, что n относится к такому объекту, который программа поменять не может. Например, присваивание типа такого выдаст ошибку компиляции:

n = 0; // ошибка, нельзя модифицировать n ++n; // ошибка, нельзя модифицировать n

Как выражение, ссылающееся на const object, такой как n, чем-то отличается от rvalue? В конце концов, если переписать эти выражения для литерального целого числа вместо n. то получим ту же самую ошибку:

7 = 0; // ошибка, нельзя модифицировать литерал ++7; // ошибка, нельзя модифицировать литерал

Если Вы больше не можете модифицировать n, как будто это rvalue, то почему нельзя сказать, что n также является rvalue? Различие состоит в том, что Вы можете получить адрес от const object, но получить адрес от литерального целого нельзя. Например:

int const *p; . p = &n; // ok p = &7; // ошибка

Заметьте, что p, объявленный чуть выше, должен быть указателем именно на const int. Если Вы пропустите ключевое слово const в объявлении указателя, например напишете так:

int *p;

то тогда следующее присваивание выдаст ошибку:

p = &n; // ошибка, недопустимое преобразование

Когда Вы получаете адрес от объекта const int, то получаете значение типа "указатель на const int," которое нельзя преобразовать в "указатель на int" без использования приведения типа (cast). Вот пример такого приведения типа:

p = (int *)&n; // как бы ok, если мы в здравом уме и понимаем, что делаем

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

Таким образом выражение, которое относится к объекту const, действительно является lvalue, а не rvalue. Однако это специальный вид lvalue, называемый немодифицируемый lvalue (non-modifiable lvalue), который нельзя использовать для модифицирования объекта, на который ссылается lvalue. Это в отличие от поддающегося изменению lvalue (который декларирован без const), который можно использовать для изменения объекта, на который ссылается lvalue.

Поскольку теперь может влиять фактор наличия ключевого слова const, то больше не будет точным называть левую часть оператора присваивания, как lvalue. Скорее, его нужно назвать возможным для изменения lvalue (modifiable lvalue). Фактически, каждый арифметический оператор присваивания, такой как += и *=, требует модифицируемого lvalue в качестве левого операнда. Для всех скалярных типов:

x += y; // арифметическое присваивание
x = x + y; // простое присваивание

за исключением того, что x оценивается только один раз. Поскольку x в этом арифметическом присваивании должен быть модифицируемым lvalue, также должно быть и в простом присваивании. Не каждый оператор, который требует операнда lvalue, требует именно модифицируемого lvalue. Унарный оператор & принимает в качестве операнда как modifiable lvalue, так и non-modifiable lvalue. Например, если задано:

int m; int const n = 10;

то &m является допустимым выражением, которое вернет тип "указатель на int", и &n будет также допустимым выражением, возвращающим результат "указатель на const int".

[Что же на самом деле является немодифицируемым]

Ранее я говорил, что non-modifiable lvalue это lvalue, который Вы не можете использовать, чтобы изменить объект. Заметьте, что я не говорил, что non-modifiable lvalue относится к объекту, который Вы не можете изменить - я сказал, что Вы не можете использовать lvalue, чтобы изменить объект. (Примечание переводчика: как я люблю эти извращенские головоломки языка C!) Различие тонкое, но тем не менее важное, как будет показано в следующем примере. Предположим:

int n = 0; int const *p; . p = &n;

Тут мы имеем, что p указывает на n, так что и *p, и просто n являются двумя разными выражениями, ссылающимися на один и тот же объект. Однако, *p и n имеют разные типы. Как было рассказано в заметке "What const Really Means" (Что на самом деле означает const) [1], присваивание использует конверсию квалификации, чтобы преобразовать значение типа "указатель на int" в значение типа "указатель на const int". Выражение n имеет тип "не const int". Это модифицируемое lvalue, так что Вы можете модифицировать объект, на которое оно указывает:

n += 2;

С другой стороны, p имеет тип "указатель на const int", поэтому *p имеет тип const int. Выражение *p является немодифицируемым lvalue, и Вы не можете использовать *p для того, чтобы модифицировать n (даже если Вы можете использовать для модификации выражение n):

*p += 2; //ошибка!

Такова семантика определения константы const в C и C ++.

[Общие выводы]

Каждое выражение C и C++ является либо lvalue, либо rvalue. Lvalue является выражением, которое обозначает объект (ссылается на него с указателем или без). Каждое lvalue бывает, в свою очередь, или модифицируемым, или немодифицируемым. К rvalue относится любое выражение, которое не является lvalue. Оперативно различия между rvalue и lvalue можно свести к тезисам:

- modifiable lvalue является адресуемым (может быть операндом унара &) и присваиваемым (может являться левым операндом =).
- non-modifiable lvalue также является адресуемым. Но оно не является присваиваемым.
- rvalue не является ни адресуемым, ни присваиваемым.

И снова, как уже упоминалось, все это относится только к rvalue не классового (non-class) типа. Классы в C++ портят эти понятия еще более.

[Ссылки]

1. Lvalues and Rvalues site:embedded.com.
2. Non-modifiable Lvalues site:embedded.com.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *