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

  Юрий  | 

    | 

  Обновл. 28 Июн 2019  | 

 3309

 ǀ   2 

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

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

Ссылки 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 (30 оценок, среднее: 4,97 из 5)
Загрузка...

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

  1. Аватар Андрей:

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

    1. Юрий Юрий:

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

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

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