Из Википедии, бесплатной энциклопедии
Перейти к навигации Перейти к поиску

В C ++ компьютерного программирования , копирование элизии относится к оптимизации компилятора технологии , которая исключает ненужное копирование объектов . Стандарт языка C ++ обычно позволяет реализациям выполнять любую оптимизацию при условии, что наблюдаемое поведение результирующей программы такое же, как если бы программа была выполнена точно так, как предписано стандартом, т.е.

Стандарт также описывает несколько ситуаций, когда копирование можно исключить, даже если это изменит поведение программы, наиболее распространенной из которых является оптимизация возвращаемого значения. Другая широко применяемая оптимизация, описанная в стандарте C ++ , - это когда временный объект типа класса копируется в объект того же типа. [1] В результате инициализация копирования обычно эквивалентна прямой инициализации с точки зрения производительности, но не с точки зрения семантики; для инициализации копирования по- прежнему требуется доступный конструктор копирования . [2] Оптимизацию нельзя применить к временному объекту, привязанному к ссылке.

Пример [ править ]

#include  <iostream>int  n  =  0 ;struct  C  {  явный  C ( int )  {}  C ( const  C & )  {  ++ n ;  }  // конструктор копирования имеет видимый побочный эффект };  // модифицирует объект со статической продолжительностью храненияint  main ()  {  C  c1 ( 42 );  // прямая инициализация, вызывает C :: C (int)  C  c2  =  C ( 42 );  // инициализация копирования, вызывает C :: C (const C &) std :: cout  <<  n  <<  std :: endl ;  // выводит 0, если копия была опущена, в противном случае - 1 }

Согласно стандарту подобная оптимизация может быть применена к объектам быть брошена и поймано , [3] [4] , но неясно , относится ли оптимизация как к копии от брошенного объекта на объект исключения , и копию с исключения объект для объекта, объявленного в объявлении исключения в предложении catch . Также неясно, применяется ли эта оптимизация только к временным объектам или также к именованным объектам. [5] Учитывая следующий исходный код:

#include  <iostream>struct  C  {  C ()  = по  умолчанию ;  C ( const  C & )  {  std :: cout  <<  "Hello World! \ N " ;  } };void  f ()  {  C  c ;  бросить  c ;  // копирование именованного объекта c в объект исключения. }  // Неясно, может ли эта копия быть опущена (опущена).int  main ()  {  попробуйте  {  f ();  }  catch  ( C  c )  {  // копирование объекта исключения во временный объект  // объявления исключения.  }  // Также неясно, может ли эта копия быть опущена (опущена). }

Поэтому соответствующий компилятор должен создать программу, которая печатает «Hello World!». дважды. В текущей версии стандарта C ++ ( C ++ 11 ) проблемы были устранены, что позволило исключить как копию именованного объекта в объект исключения, так и копию в объект, объявленный в обработчике исключения. [5]

GCC предоставляет ‑fno‑elide‑constructorsвозможность отключить копирование-исключение. Эта опция полезна, чтобы наблюдать (или не наблюдать) эффекты оптимизации возвращаемого значения или других оптимизаций, при которых копии опускаются. Обычно не рекомендуется отключать эту важную оптимизацию.

Оптимизация возвращаемого значения [ править ]

В контексте C ++ Язык программирования , оптимизация возвращаемого значения ( РВО ) является оптимизация компилятора , который включает в себя устранение временного объекта , созданного для проведения функции , возвращаемого значения. [6] RVO может изменять наблюдаемое поведение результирующей программы в соответствии со стандартом C ++ . [7]

Резюме [ править ]

В общем, стандарт C ++ позволяет компилятору выполнять любую оптимизацию при условии, что результирующий исполняемый файл демонстрирует такое же наблюдаемое поведение, как если бы (т. Е. Притворяться) все требования стандарта были выполнены. Это обычно называют « правилом как будто ». [8] Термин « оптимизация возвращаемого значения» относится к специальному пункту стандарта C ++, который идет даже дальше, чем правило «как если бы»: реализация может опустить операцию копирования, являющуюся результатом оператора возврата , даже если конструктор копирования имеет сторону эффекты . [1]

В следующем примере демонстрируется сценарий, в котором реализация может исключить одну или обе создаваемые копии, даже если конструктор копирования имеет видимый побочный эффект (печать текста). [1] Первый экземпляр , который может быть устранен является тот , где безымянным временным Cможет быть скопированы в функцию f«ы возвращаемого значения . Вторая копия, которую можно удалить, - это копия временного объекта, возвращенная fв obj.

#include  <iostream>struct  C  {  C ()  = по  умолчанию ;  C ( const  C & )  {  std :: cout  <<  "Копия сделана. \ N " ;  } };C  f ()  {  вернуть  C (); }int  main ()  {  std :: cout  <<  "Hello World! \ n " ;  C  obj  =  f (); }

В зависимости от компилятора и настроек этого компилятора результирующая программа может отображать любой из следующих выходных данных:

Привет мир!Копия была сделана.Копия была сделана.
Привет мир!Копия была сделана.
Привет мир!

Фон [ править ]

Возврат объекта встроенного типа из функции обычно практически не связан с накладными расходами, поскольку объект обычно помещается в регистр ЦП . Возврат более крупного объекта типа класса может потребовать более дорогостоящего копирования из одной области памяти в другую. Чтобы избежать этого, реализация может создать скрытый объект в кадре стека вызывающего объекта и передать адрес этого объекта функции. Затем возвращаемое значение функции копируется в скрытый объект. [9] Таким образом, такой код:

struct  Data  {  char  bytes [ 16 ];  };Данные  F ()  {  Результат данных  = {}; // генерировать результат return result ; }     int  main ()  {  Данные  d  =  F (); }

может сгенерировать код, эквивалентный этому:

struct  Data  {  char  bytes [ 16 ]; };Данные *  F ( Данные *  _hiddenAddress )  {  Результат данных  = {}; // копируем результат в скрытый объект * _hiddenAddress = result ; return _hiddenAddress ; }        int  main ()  {  Данные  _hidden ;  // создать скрытый объект  Data  d  =  * F ( & _hidden );  // копируем результат в d }

что вызывает Dataдвойное копирование объекта.

На ранних этапах развития C ++ неспособность языка эффективно возвращать объект типа класса из функции считалась слабым местом. [10] Примерно в 1991 году Уолтер Брайт реализовал метод минимизации копирования, эффективно заменив скрытый объект и именованный объект внутри функции на объект, используемый для хранения результата: [11]

struct  Data  {  char  bytes [ 16 ]; };void  F ( Data *  p )  {  // генерируем результат прямо в * p }int  main ()  {  Данные  d ;  F ( & d ); }

Брайт реализовал эту оптимизацию в своем компиляторе Zortech C ++ . [10] Этот конкретный метод был позже придуман как «оптимизация именованного возвращаемого значения», имея в виду тот факт, что копирование именованного объекта исключается. [11]

Поддержка компилятора [ править ]

Оптимизация возвращаемого значения поддерживается большинством компиляторов. [6] [12] [13] Однако могут быть обстоятельства, при которых компилятор не может выполнить оптимизацию. Один из распространенных случаев - когда функция может возвращать объекты с разными именами в зависимости от пути выполнения: [9] [12] [14]

#include  <строка>std :: string  F ( bool  cond  =  false )  {  std :: string  first ( "первый" );  std :: string  second ( "второй" );  // функция может возвращать один из двух именованных объектов  // в зависимости от своего аргумента. RVO не может быть применен  возврат  cond  ?  первый  :  второй ; }int  main ()  {  std :: строка  результат  =  F (); }

Внешние ссылки [ править ]

  • Скопируйте elision на cppreference.com

Ссылки [ править ]

  1. ^ а б в ИСО / МЭК (2003). ISO / IEC 14882: 2003 (E): Языки программирования - C ++ §12.8 Копирование объектов класса [class.copy] параграф. 15
  2. Перейти ↑ Sutter, Herb (2001). Более исключительный C ++ . Эддисон-Уэсли. CS1 maint: обескураженный параметр ( ссылка )
  3. ^ ISO / IEC (2003). ISO / IEC 14882: 2003 (E): Языки программирования - C ++ §15.1 Выдача исключения [except.throw] п. 5
  4. ^ ISO / IEC (2003). ISO / IEC 14882: 2003 (E): Языки программирования - C ++ §15.3 Обработка исключения [except.handle] п. 17
  5. ^ a b «Отчеты о дефектах стандартного основного языка C ++» . WG21 . Проверено 27 марта 2009 . CS1 maint: обескураженный параметр ( ссылка )
  6. ^ a b Мейерс, Скотт (1995). Более эффективный C ++ . Эддисон-Уэсли.
  7. ^ Alexandrescu, Андрей (2003-02-01). «Конструкторы перемещения» . Журнал доктора Добба . Проверено 25 марта 2009 . CS1 maint: обескураженный параметр ( ссылка )
  8. ^ ISO / IEC (2003). ISO / IEC 14882: 2003 (E): Языки программирования - C ++ §1.9 Выполнение программы [intro.execution] пункт. 1
  9. ^ а б Булька, Дов; Дэвид Мэйхью (2000). Эффективный C ++ . Эддисон-Уэсли. ISBN 0-201-37950-3.
  10. ^ a b Липпман, Стэн (2004-02-03). «Оптимизация возвращаемого значения имени» . Microsoft . Проверено 23 марта 2009 . CS1 maint: обескураженный параметр ( ссылка )
  11. ^ a b «Глоссарий языка программирования D 2.0» . Цифровой Марс . Проверено 23 марта 2009 . CS1 maint: обескураженный параметр ( ссылка )
  12. ^ a b Шукри, Айман Б. (октябрь 2005 г.). «Оптимизация именованных возвращаемых значений в Visual C ++ 2005» . Microsoft . Проверено 20 марта 2009 . CS1 maint: обескураженный параметр ( ссылка )
  13. ^ «Параметры, управляющие диалектом C ++» . GCC . 2001-03-17 . Проверено 20 января 2018 . CS1 maint: обескураженный параметр ( ссылка )
  14. ^ Хиннант, Ховард; и другие. (2002-09-10). «N1377: Предложение о добавлении поддержки семантики перемещения в язык C ++» . WG21 . Проверено 25 марта 2009 . CS1 maint: обескураженный параметр ( ссылка )