Что такое copy elision? Сколько раз будет вызван конструктор/деструктор у объекта, которого возвращают по значению?cplus-84

Что такое Copy Elision?

Copy Elision (пропуск копирования) - это оптимизация компилятора, которая позволяет избежать избыточных операций копирования/перемещения объектов в определенных ситуациях. Это одна из немногих оптимизаций, разрешенных стандартом C++ даже когда это изменяет наблюдаемое поведение программы (поскольку конструкторы копирования/перемещения могут иметь побочные эффекты).

Основные случаи применения Copy Elision

  1. Возврат по значению (Return Value Optimization - RVO)
  2. Именованная Return Value Optimization (NRVO)
  3. Инициализация объекта из временного значения

Пример с возвратом по значению

class Widget {
public:
    Widget() { std::cout << "Constructor\n"; }
    Widget(const Widget&) { std::cout << "Copy Constructor\n"; }
    ```Widget() { std::cout << "Destructor\n"; }
};

Widget createWidget() {
    return Widget();  // Создается временный объект
}

int main() {
    Widget w = createWidget();
    return 0;
}

Сколько раз вызываются конструкторы/деструкторы?

В зависимости от ситуации и поддержки компилятором оптимизаций:

  1. Без оптимизаций (теоретически):

    • Конструктор в createWidget()
    • Конструктор копирования при возврате значения
    • Конструктор копирования при инициализации w
    • 3 деструктора для всех временных объектов
  2. С RVO (реальный случай в современных компиляторах):

    • Конструктор вызывается 1 раз (объект создается сразу в нужном месте)
    • Деструктор вызывается 1 раз (для финального объекта)
    • Никаких копирований не происходит

Важные детали

  • Начиная с C++17: Для некоторых случаев (как в примере выше) copy elision гарантирован стандартом (обязателен для компиляторов).
  • До C++17: Copy elision был разрешенной, но не обязательной оптимизацией.
  • Когда не применяется:
    • Если возвращаемый объект имеет имя (NRVO может помочь, но не гарантирован)
    • Если возвращаемый объект зависит от пути выполнения

Пример с именованным объектом

Widget createWidget(bool flag) {
    Widget a, b;
    if (flag) return a;
    return b;  // NRVO может не сработать
}

Резюмируем

Copy Elision - важная оптимизация, минимизирующая накладные расходы при работе с временными объектами. В современных C++ (17+) для простых случаев возврата по значению гарантируется 1 вызов конструктора и 1 вызов деструктора без промежуточных копирований. Для более сложных случаев NRVO может помочь, но не гарантирован. Всегда учитывайте это при проектировании классов с "тяжелыми" конструкторами копирования.