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