Что такое rvalue и lvalue

Что такое rvalue и lvalue

Каждое выражение C++, с типом, а также входит в категории значений. Every C++ expression has a type, and belongs to a value category. Значение категории являются основой для правил, компиляторы должны следовать при создании, копирование и перемещение временные объекты во время вычисления выражения. The value categories are the basis for rules that compilers must follow when creating, copying, and moving temporary objects during expression evaluation.

Стандарт C ++ 17 определяет категории значение выражения следующим образом: The C++17 standard defines expression value categories as follows:

  • Объект glvalue представляет собой выражение, вычисление которого определяет удостоверение объекта, битового поля или функции. A glvalue is an expression whose evaluation determines the identity of an object, bit-field, or function.
  • Объект prvalue представляет собой выражение, вычисление которого инициализирует объект или битовому полю или вычисляет значение операнда оператора, как указано в контексте, в котором он отображается. 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.
  • Xvalue является glvalue, который обозначает объект или битового поля, ресурсы которых может многократно использоваться (обычно, поскольку он находится в конце его времени существования). 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). Пример Некоторые типы выражений, включающих ссылки rvalue (8.3.2) дают xvalues, такие как вызов функции, возвращаемый тип которых является ссылкой rvalue или преобразование в ссылочный тип rvalue. Example: Certain kinds of expressions involving rvalue references (8.3.2) yield xvalues, such as a call to a function whose return type is an rvalue reference or a cast to an rvalue reference type.
  • Lvalue является glvalue, который не является xvalue. An lvalue is a glvalue that is not an xvalue.
  • Rvalue prvalue или xvalue. An rvalue is a prvalue or an xvalue.

Следующей схеме показаны связи между категориями: The following diagram illustrates the relationships between the categories:

Lvalue имеет адрес, который можно получить доступ к программе. An lvalue has an address that your program can access. Примеры выражений lvalue включают имена переменных, включая const переменных, элементов массива, функция вызовы, которые возвращают ссылки lvalue, битовые поля, объединения и членов класса. Examples of lvalue expressions include variable names, including const variables, array elements, function calls that return an lvalue reference, bit-fields, unions, and class members.

Выражение prvalue не имеет адреса, доступном в программе. A prvalue expression has no address that is accessible by your program. Примеры выражений prvalue включают литералы, вызовы функций, возвращающих тип без ссылок и временные объекты, созданные во время вычислений выражения, но доступен только компилятором. Examples of prvalue expressions include literals, function calls that return a non-reference type, and temporary objects that are created during expression evalution but accessible only by the compiler.

Читайте также:  Как выделить абзац в ворде 2010

Выражение xvalue имеет адрес, станут недоступными в программе, но может использоваться для инициализации ссылкой rvalue, который предоставляет доступ к выражению. An xvalue expression has an address that no longer accessible by your program but can be used to initialize an rvalue reference, which provides access to the expression. Примеры вызовов функций, которые возвращают ссылку rvalue и индекс массива, член и указателя выражения элементов, где массива или объекта является ссылкой rvalue. Examples include function calls that return an rvalue reference, and the array subscript, member and pointer to member expressions where the array or object is an rvalue reference.

Пример Example

В следующем примере показано несколько правильных и неправильных способов использования значений lvalue и rvalues. The following example demonstrates several correct and incorrect usages of lvalues and rvalues:

В примерах этого раздела показано правильное и неправильное использование при неперегруженных операторах. The examples in this topic illustrate correct and incorrect usage when operators are not overloaded. Перегрузив операторы, можно преобразовать выражение, такое как j * 4 , в значение lvalue. By overloading operators, you can make an expression such as j * 4 an lvalue.

Условия lvalue и rvalue часто используются при ссылке на ссылки на объекты. The terms lvalue and rvalue are often used when you refer to object references. Дополнительные сведения о ссылках см. в разделе декларатор ссылки Lvalue: & и декларатор ссылки Rvalue: & &. For more information about references, see Lvalue Reference Declarator: & and Rvalue Reference Declarator: &&.

    Статьи, 3 августа 2018 в 20:18

В этой статье разобраны основные преимущества и нюансы move семантики в C++11 и старше. Всё описанное в этой статье было проверено на компиляторе Clang 6.0.1 с библиотекой libc++ на x86, GNU/Linux.

Введение в move

Move семантика позволяет переместить объект вместо его копирования для увеличения производительности. Проще всего понять семантику перемещения на примере. В качестве этого примера будет использоваться класс String :

При передаче объекта этого класса в функцию, принимающую его по значению, — назовём её by_value() — произойдёт следующее:

Получается 4 обращения к аллокатору, что достаточно накладно. Но если объект String больше не понадобится, а функцию by_value() менять нельзя, то можно переместить объект, а не копировать. Для этого необходимо написать конструктор перемещения для класса String :

Параметр конструктора перемещения other , во-первых, неконстантный, т.к. конструктор его изменяет; во-вторых, является rvalue -ссылкой ( && ), а не lvalue -ссылкой ( & ). Об их отличиях будет сказано далее. Сам конструктор переносит Си-строку с other на this , делая other пустым.

Читайте также:  Как проверить питание на проводе

Конструктор перемещения в общем случае не медленнее, а зачастую даже быстрее конструктора копирования, но ничего не мешает программисту поместить sleep(10’000) в конструктор перемещения.

Searchanise, Ульяновск, от 150 000 ₽

Для вызова конструктора перемещения вместо конструктора копирования можно использовать std::move() . Теперь пример выглядит следующим образом:

Количество обращений к аллокатору уменьшилось вдвое!

rvalue и lvalue

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

Это «могут быть» лучше всего демонстрируют следующие два примера:

Этот код работает не так, как от него ожидается. std::move() всё ещё конвертирует lvalue в rvalue , но конвертация сохраняет все модификаторы, в том числе и const . Затем компилятор выбирает среди двух конструкторов класса String самый подходящий. Из-за того что компилятор не может отправить const rlvaue туда, где ожидается non-const rvalue , он выбирает конструктор копирования, и const rvalue конвертируется обратно в const lvalue . Вывод: следите за модификаторами объектов, т.к. они учитываются при выборе одной из перегрузок функции.

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

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

Универсальные ссылки

Универсальные ссылки могут быть как rvalue -, так и lvalue -ссылкой в зависимости от аргументов или результата функции. Используются в шаблонах и в auto&& :

Компилятор на основе этого шаблона генерирует 2 функции, одна из которых принимает lvalue , другая — rvalue , если они будут использоваться. Если программист хочет использовать перемещение для rvalue -ссылки и простое копирование для lvalue -ссылки, то он может использовать std::forward() , который приводит свой аргумент к rvalue только тогда, когда его тип является rvalue -ссылкой. std::forward() требует явного указания шаблонного параметра даже с автоматическим выводом шаблонов в С++17.

Универсальная ссылка обязана быть шаблонным параметром (отсюда такое странное определение шаблона функции в примере) в формате T&& . Например, std::vector && уже не универсальная, а rvalue -ссылка.

Свёртывание ссылок

На самом деле разницы между rvalue -ссылкой и универсальной ссылкой нет. Универсальная ссылка — лишь удобная абстракция над rvalue -ссылкой, которой многие пользуются. Но как тогда rvalue -ссылка превращается в lvalue -ссылку при lvalue аргументе? Всё дело в свёртывании ссылок.

Читайте также:  Щелкает блок питания компьютера и не включается

При вызове template_func(string) компилятор генерирует следующий заголовок функции:

И получается ссылка на ссылку! Вручную так сделать нельзя, но шаблоны могут. Далее компилятор свёртывает ссылку на ссылку. Свертывание производится по следующему правилу: результатом свертывания будет rvalue -ссылка только тогда, когда обе ссылки являются rvalue -ссылками.

Именно из-за этого безобразия проще использовать абстракцию универсальных ссылок.

Подробнее о move семантике и rvalue можно узнать из книги С. Мейерса «Эффективный и современный С++: 42 рекомендации по использованию С++ 11 и С++14» ISBN: 978-5-8459-2000-3.

Copy/move elision

Copy/move elision — это оптимизация, при которой компилятор может убрать некоторые вызовы конструктора копирования и деструктора, но в данный момент только при возврате объекта из функции, и только если тип возвращаемого объекта полностью совпадает с типом функции.

Поэтому при возврате из функции использование std::move() может снизить производительность, ограничив компилятор в Copy elision оптимизации, ведь отсутствие конструктора быстрее, чем конструктор перемещения.

Здесь std::move() только замедляет код, добавляя лишний вызов String(String&& other) и

В C++20 copy/move elision может быть расширен, благодаря чему в некоторых случаях использование std::move() также снизит производительность. Подробнее о расширении copy/move elision можно узнать из видеозаписи со встречи рабочей группы по стандартизации С++ в московском офисе Яндекса.

Добрый день. Если я не ошибаюсь, то lvalue-ссылка &r может ссылаться на адрес памяти именованного объекта (переменной). Так вот, rvalue-ссылка &&rr это тоже самое что и lvalue-ссылка, только при этом может ссылаться на временный объект? (например литерал 5 )

показываю на примере то что они похожи:

показываю на примере что может ссылаться на временный объект:

Кто нибудь может пояснить? Если они почти одинаково работают, то в чем профит использования rvalue ссылок в семантике перемещения. Тема для меня очень сложная. Не могу понять концепцию..

  • Вопрос задан более трёх лет назад
  • 1504 просмотра

то в чем профит использования rvalue ссылок в семантике перемещения

В том, что они позволяют отличить временные объекты, из которых можно утащить кое-что полезное, от постоянных, с которыми так лучше не поступать. Постоянные объекты будут и дальше копироваться конструкторами копирования. А вот для временных будет выбран конструктор перемещения, если таковой определен.

Использование std::move — это возможность назвать некоторый постоянный с точки зрения языка объект (например, обыкновенную локальную переменную) временным, когда есть уверенность, что его содержимое не нужно далее по коду. Для него все также будет вызван деструктор (т.е. объект, попавший под move-семантику НЕ считается разрушенным после этого), но после процедуры перемещения считается, что объект находится в некотором неопределенном ("пустом"), но валидном состоянии.

Ссылка на основную публикацию
Adblock detector