Урок №198. Посилання r-value

  Юрій  | 

  Оновл. 31 Бер 2021  | 

 109

Ми вже раніше розглядали 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 Зірок (1 оцінок, середня: 5,00 з 5)
Loading...

Залишити відповідь

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