Поліпшення повернення значення (англ. return value optimization, RVO) чи просто ППЗ — це техніка оптимізації, яку застосовує компілятор, що серед іншого виключає створення тимчасового об'єкта для збереження значення, що вертається з функції. В C++, вона особливо примітна тим, що дозволяє змінити поведінку отриманої програми.
Підсумок Редагувати
Загалом, стандарт С++ дозволяє компіляторам застосовувати будь-які оптимізації, допоки отриманий виконуваний файл поводиться так, наче всі вимоги стандарту було виконано. Цей підхід зазвичай згадується як правило "так наче" (англ. as-if rule). Термін поліпшення повернення значення звертається до пункту в ISO/IEC 14882, який дозволяє реалізації опустити копіювання, що відбувається через інструкцію повернення, навіть якщо конструктор копіювання має побічні ефекти,, хоча це й не дозволяється самим правилом так наче.
Наступний приклад показує хід дій, за якого реалізація може позбутись одного чи двох копіювань, навіть якщо конструктор копіювання має видимі побічні ефекти, приміром, роздрук тексту. Перше копіювання якого можна уникнути - копіювання створеного екземпляру класу C()
у значення, що поверне функція f
. Друге - копіювання тимчасового об'єкта повернутого функцією f
у obj
.
#include <iostream> struct C { C() {} C(const C&) { std::cout << "Відбулось копіювання.\n"; } }; C f() { return C(); } int main() { std::cout << "Здоровенькі були!\n"; C obj = f(); }
Залежно від компілятора та його налаштувань, програма може вивести такий текст:
Здоровенькі були! Відбулось копіювання. Відбулось копіювання.
Здоровенькі були! Відбулось копіювання.
Здоровенькі були!
Пояснення Редагувати
При повернені вбудованого типу з функції додаткових операцій зазвичай не виконується, оскільки об'єкт передається через регістр. Вертання більших об'єктів (екземпляру деякого класу) може вимагати копіювання з однієї місцини в пам'яті до іншої. Для цього у стековому кадрі створюється прихований об'єкт, адреса якого передається у функцію. Значення що повертається з функції копіюється в цей об'єкт за наданою адресою. Отже, для коду на кшталт:
struct Data { char bytes[16]; }; Data f() { Data result = {}; // утворення результату return result; } int main() { Data d = f(); }
компілятор згенерує таке:
struct Data { char bytes[16]; }; Data * f(Data * _hiddenAddress) { Data result = {}; // копіювання результату в прихований об'єкт *_hiddenAddress = result; return _hiddenAddress; } int main() { Data _hidden; // створити прихований об'єкт Data d = *f(&_hidden); // копіювати результат в d }
Як бачимо, двічі виконується копіювання об'єкта Data
.
На ранніх стадіях розвитку C++, нездатність мови ефективно повернути об'єкт класу з функції вважалась одним з її недоліків. Близько 1991, Волтер Брайт винайшов спосіб зменшення кількості копіювань, використовуючи замість двох об'єктів (прихованого та локальної змінної функції f) один - у який зберігається результат:
struct Data { char bytes[16]; }; void f(Data *p) { // записуємо результат одразу в *p } int main() { Data d; f(&d); }
Брайт реалізував своє поліпшення в компіляторі Zortech C++. Цей особливий спосіб був з часом названий «Поліпшення повернення іменованого значення» (англ. named return value optimization), вказуючи на той факт, що копіювання іменованого об'єкта скасоване.
Підтримка компіляторами Редагувати
Поліпшення повернення значення підтримується більшістю компіляторів. Однак можливі умови, коли компілятор не може виконати поліпшення. Найпоширенішим є приклад, коли функція повертає різні значення в залежності від шляху виконання програми:
#include <string> std::string f(bool cond = false) { std::string first("first"); std::string second("second"); // функція може повернути один із двох іменованих об'єктів // залежно від параметра. ППЗ не може бути застосоване return cond ? first : second; } int main() { std::string result = f(); }
Примітки Редагувати
- ↑ Meyers, Scott (1996). More Effective C++. Addison Wesley.
- Alexandrescu, Andrei (1 лютого 2003). Move Constructors. Dr. Dobbs Journal. Архів оригіналу за 14 липня 2013. Процитовано 25 березня 2009.
- ↑ ISO/IEC (2003). ISO/IEC 14882:2003(E): Programming Languages - C++ §1.9 Program execution [intro.execution] para. 1
- ↑ ISO/IEC (2003). ISO/IEC 14882:2003(E): Programming Languages - C++ §12.8 Copying class objects [class.copy] para. 15
- ↑ Bulka, Dov; David Mayhew (2000). Efficient C++. Addison-Wesley. ISBN 0-201-37950-3.
- ↑ Lippman, Stan. The Name Return Value Optimization. Stan Lippman. Архів оригіналу за 14 липня 2013. Процитовано 23 березня 2009.
- ↑ Glossary D Programming Language 2.0. Digital Mars. Архів оригіналу за 14 липня 2013. Процитовано 23 березня 2009.
- ↑ Shoukry, Ayman B. Named Return Value Optimization in Visual C++ 2005. Microsoft. Архів оригіналу за 14 липня 2013. Процитовано 20 березня 2009.
- Options Controlling C++ Dialect. GCC. 17 березня 2001. Архів оригіналу за 14 липня 2013. Процитовано 20 березня 2009.
- Hinnant, Howard; et al. (10 вересня 2002). . WG21. Архів оригіналу за 8 лютого 2007. Процитовано 25 березня 2009.