Урок №190. Ссылки r-value

  Юрий  | 

  |

  Обновл. 8 Окт 2021  | 

 50879

 ǀ   11 

Мы уже ранее рассматривали l-values и r-values. Тогда мы говорили, что вам не нужно слишком беспокоиться о них. И это было правдой до C++11. Сейчас же, для понимания семантики перемещения, нам нужно пересмотреть эту тему.

l-values и r-values

Несмотря на то, что в обоих терминах есть слово «value» (значение), l-values и r-values на самом деле являются не свойствами значений, а скорее свойствами выражений.

Каждое выражение в языке C++ имеет два свойства: тип и категорию значения (определяет, можно ли результат выражения присвоить другому объекту). В C++03 и в более ранних версиях С++ l-values ​​и r-values ​​были единственными категориями значений.

О l-value проще всего думать, как о функции, объекте или переменной (или выражении, результатом которого является функция, объект или переменная), которая имеет свой адрес памяти. Изначально l-values были определены как «значения, которые должны находиться в левой части операции присваивания». Однако позже в язык С++ было добавлено ключевое слово const, и l-values были разделены на две подкатегории:

   Модифицируемые l-values, которые можно изменить (например, переменной x можно присвоить другое значение).

   Немодифицируемые l-values, которые являются const (например, константа PI).

О r-value проще всего думать, как «обо всем остальном, что не является l-value». Это литералы (например, 5), временные значения (например, x + 1) и анонимные объекты (например, Fraction(7, 3)). r-values имеют область видимости выражения (уничтожаются в конце выражения, в котором находятся) и им нельзя что-либо присвоить. Этот запрет на присваивание имеет смысл, так как присваивая значение мы вызываем в объекта побочные эффекты.

А поскольку r-values имеют область видимости выражения, то, если бы мы присваивали какое-либо значение для r-value, r-value либо выходило бы из области видимости, прежде чем у нас была бы возможность использовать присвоенное значение в следующем выражении (что делает операцию присваивания бесполезной), либо нам пришлось бы использовать переменную с побочным эффектом, который возникал бы больше одного раза в выражении (что, как вы уже должны знать, привело бы к неопределенным результатам!).

Для поддержки семантики перемещения в C++11 ввели 3 новые категории значений:

   pr-values;

   x-values;

   gl-values.

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

Ссылки l-value


До версии C++11 существовал только один тип ссылок, его называли просто — «ссылка». В C++11 этот тип ссылки еще называют «ссылкой l-value». Ссылки l-value могут быть инициализированы только изменяемыми l-values.

Ссылки l-value Могут ли быть инициализированы? Могут ли значения изменяться?
Изменяемые l-values Да Да
Неизменяемые l-values Нет Нет
r-values Нет Нет

Ссылки l-value на константные объекты могут быть инициализированы с помощью как l-values, так и r-values. Однако эти значения не могут быть изменены (константы не изменяют свои значения).

Ссылки l-value на const Могут ли быть инициализированы? Могут ли значения изменяться?
Изменяемые l-values Да Нет
Неизменяемые l-values Да Нет
r-values Да Нет

Ссылки l-value на константные объекты особенно полезны, так как позволяют передавать аргументы любого типа (l-value или r-value) в функцию без выполнения копирования аргумента.

Ссылки r-value

В C++11 добавили новый тип ссылок — ссылки r-value. Ссылки r-value — это ссылки, которые инициализируются только значениями r-values. Хотя ссылка l-value создается с использованием одного амперсанда, ссылка r-value создается с использованием двойного амперсанда:

Ссылки r-value не могут быть инициализированы значениями l-values.

Ссылки r-value Могут ли быть инициализированы? Могут ли значения изменяться?
Изменяемые l-values Нет Нет
Неизменяемые l-values Нет Нет
r-values Да Да

И ссылки r-value на константные объекты:

Ссылки r-value на const Могут ли быть инициализированы? Могут ли значения изменяться?
Изменяемые l-values Нет Нет
Неизменяемые l-values Нет Нет
r-values Да Нет

Ссылки r-value имеют два полезных свойства:

   Они увеличивают продолжительность жизни объекта, которым инициализируются, до продолжительности жизни ссылки r-value (ссылки l-value на константные объекты также могут это делать).

   Неконстантные ссылки r-value позволяют нам изменять значения r-values, на которые указывают ссылки r-value!

Рассмотрим следующую программу:

Результат:

4/7

Создаваемый анонимный объект Fraction(4, 7) обычно вышел бы из области видимости в конце выражения, в котором он определен. Однако, так как мы инициализируем ссылку r-value этим анонимным объектом, то его продолжительность жизни увеличивается до продолжительности жизни самой ссылки r-value, т.е. до конца функции main(). Затем мы используем ссылку r-value для вывода значения анонимного объекта класса Fraction.

Теперь рассмотрим менее наглядный пример:

Результат выполнения программы:

12

Хотя это может показаться странным, но при инициализации ссылки r-value литералом, создается временный объект, на который ссылается ссылка r-value (она не ссылается на сам литерал).

Ссылки r-value не очень часто используются так, как это представлено в вышеприведенных примерах.

Ссылки r-value в качестве параметров функции


Ссылки r-value чаще всего используются в качестве параметров функции. Это наиболее полезно при перегрузке функций, когда мы хотим, чтобы выполнение функции отличалось в зависимости от аргументов (l-values или r-values). Например:

Результат выполнения программы:

l-value reference to const
r-value reference

Как вы можете видеть, при передаче l-value, выполняется перегрузка функции с ссылкой l-value в качестве параметра, а при передаче r-value — выполняется перегрузка функции с ссылкой r-value в качестве параметра.

Зачем это может нам понадобиться? Более детально об этом мы поговорим на следующем уроке. Излишне говорить, что это важная часть семантики перемещения.

Возврат ссылки r-value

Вы почти никогда не должны возвращать ссылку r-value из функции по той же причине, по которой вы почти никогда не должны возвращать ссылку l-value из функции. В большинстве случаев вы возвратите висячую ссылку (указывающую на удаленную память), а объект, на который будет ссылаться ссылка, выйдет из области видимости в конце функции.

Тест


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

Ответ

B, E и G не скомпилируются.

Оценить статью:

Звёзд: 1Звёзд: 2Звёзд: 3Звёзд: 4Звёзд: 5 (168 оценок, среднее: 4,91 из 5)
Загрузка...

Комментариев: 11

  1. Сергій:

    Юрій, Дякую Велике Вам, за вашу роботу!!!
    Курс по С++, неймовірний )

    1. Фото аватара Юрий:

      Пожалуйста))

  2. Vladyslav:

    Т.е. я так понимаю что при передаче r-value в функцию по приоритету вызывается та перегруженная функция, параметры которой принимают ссылку r-value? Ведь принять r-value может и константная ссылка l-value, как в примере

  3. Dmitro:

    Этот код выдает ошибку. То есть, ссылка переноса — lvalue?

    1. Kris:

      Абсолютно верно. Сама r-value reference, хотя и ссылается на объекты r-value, является именованным НЕ ВРЕМЕННЫМ объектом в памяти, а значит, l-value.

  4. Алексей:

    Ох , я чего-то не понимаю.
    В разделе ссылки на L-values вторая строка первой таблицы правильно заполнена?
    Если да, то получается type const& VAR = value; — не скомпилируется?
    Или я о чем-то не том думаю?

    1. Анастасия:

      Вторая строка первой таблицы заполнена правильно, Вы ведь про константную ссылку l-value пишете? Тогда надо смотреть вторую таблицу, константная ссылка l-value может быть инициализирована чем угодно, но не может потом меняться. В 89 уроке было: "ссылки на константные значения могут быть инициализированы неконстантными l-values, константными l-values и r-values". Поэтому пример С:

      — скомпилируется, но нельзя будет потом:

      т.к. в том же 89 уроке было: "Как и в случае с указателями, константные ссылки также могут ссылаться и на неконстантные переменные. При доступе к значению через константную ссылку, это значение автоматически считается const, даже если исходная переменная таковой не является"

  5. somebox:

    А почему скомпилируется D?

    1. Илья:

      Простая константная ссылка, может указывать на r-values, посмотри по таблице….

  6. Андрей:

    Здравствуйте! Спасибо Вам огромное за проделанную работу! Я хотел поинтересоваться: Вы собираетесь вести какой-то другой туториал после того, как закончите перевод материала по c++?

    1. Фото аватара Юрий:

      Привет. Спасибо, что читаешь 🙂 Честно — сам ещё не знаю, нужно этот цикл уроков допереводить, а там уже будет видно.

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

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