Поліпшення повернення значення

Матеріал з Вікіпедії — вільної енциклопедії.
Перейти до: навігація, пошук

Поліпшення повернення значення (англ. return value optimization, RVO) чи просто ППЗ — це техніка оптимізації, яку застосовує компілятор, що серед іншого виключає створення тимчасового об'єкта для збереження значення, що вертається з функції.[1] В C++, вона особливо примітна тим, що дозволяє змінити поведінку отриманої програми.[2]

Підсумок[ред.ред. код]

Загалом, стандарт С++ дозволяє компіляторам застосовувати будь-які оптимізації, допоки отриманий виконуваний файл поводиться так, наче всі вимоги стандарту було виконано. Цей підхід зазвичай згадується як правило "так наче" (англ. as-if rule).[3] Термін поліпшення повернення значення звертається до пункту в ISO/IEC 14882, який дозволяє реалізації опустити копіювання, що відбувається через інструкцію повернення, навіть якщо конструктор копіювання має побічні ефекти,[4], хоча це й не дозволяється самим правилом так наче.[3]

Наступний приклад показує хід дій, за якого реалізація може позбутись одного чи двох копіювань, навіть якщо конструктор копіювання має видимі побічні ефекти, приміром, роздрук тексту.[4] Перше копіювання якого можна уникнути це те, де 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();
}

Залежно від компілятора та його налаштувань, утворена програма може видати такий текст:

Здоровеньки були!
Відбулось копіювання.
Відбулось копіювання.
Здоровеньки були!
Відбулось копіювання.
Здоровеньки були!

Підвалини[ред.ред. код]

При повернені вбудованого типу з функції, зазвичай, немає додаткових обчислень, бо об'єкт здебільшого вміщається в регістр. Вертання більших об'єктів може вимагати копіювання з однієї місцини в пам'яті до іншої. Щоб уникнути цього, може створюватись прихований об'єкт у стековому фреймі, і передаватись його адреса у функцію. Значення що повертається з функції копіюється в цей об'єкт.[5] Отже, код на кшталт цього:

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++, нездатність мови дієво повернути об'єкт класу з функції розглядалась як слабкість.[6] Близько 1991, Волтер Брайт винайшов спосіб зменшення кількості копіювань, дієво замінивши прихований об'єкт та іменований об'єкт у функції на об'єкт використовний для збереження результату:[7]

struct Data { 
  char bytes[16]; 
};
 
void f(Data *p) {
  // записуємо результат одразу в *p
}
 
int main() {
  Data d;
  f(&d);
}

Брайт реалізував своє поліпшення в компіляторі Zortech C++.[6] Цей особливий спосіб був з часом названий «Поліпшення повернення іменованого значення» (англ. named return value optimization), вказуючи на той факт, що копіювання іменованого об'єкта скасоване.[7]

Підтримка компіляторами[ред.ред. код]

Поліпшення повернення значення підтримується на більшості компіляторів[1][8][9]. Однак можливі умови, коли компілятор не здатний учинити поліпшення. Найзагальніший приклад, коли компілятор може повернути різні значення в залежності від шляхів виконання: [8][10][5]

#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();
}

Примітки[ред.ред. код]

  1. а б Meyers, Scott (1996). More Effective C++. Addison Wesley. 
  2. Alexandrescu, Andrei (2003-02-01). «Move Constructors». Dr. Dobbs Journal. Архів оригіналу за 2013-07-15. Процитовано 2009-03-25. 
  3. а б ISO/IEC (2003). ISO/IEC 14882:2003(E): Programming Languages - C++ §1.9 Program execution [intro.execution] para. 1
  4. а б ISO/IEC (2003). ISO/IEC 14882:2003(E): Programming Languages - C++ §12.8 Copying class objects [class.copy] para. 15
  5. а б Bulka, Dov; David Mayhew (2000). Efficient C++. Addison-Wesley. ISBN 0-201-37950-3. 
  6. а б Lippman, Stan. «The Name Return Value Optimization». Stan Lippman. Архів оригіналу за 2013-07-15. Процитовано 2009-03-23. 
  7. а б «Glossary D Programming Language 2.0». Digital Mars. Архів оригіналу за 2013-07-15. Процитовано 2009-03-23. 
  8. а б Shoukry, Ayman B. «Named Return Value Optimization in Visual C++ 2005». Microsoft. Архів оригіналу за 2013-07-15. Процитовано 2009-03-20. 
  9. «Options Controlling C++ Dialect». GCC. 2001-03-17. Архів оригіналу за 2013-07-15. Процитовано 2009-03-20. 
  10. Hinnant, Howard; et al. (2002-09-10). «N1377: A Proposal to Add Move Semantics Support to the C++ Language». WG21. Процитовано 2009-03-25.