← Назад к вопросам

Как работает copy elision и RVO (Return Value Optimization)?

1.7 Middle🔥 91 комментариев
#ООП и проектирование#Сборка и инструменты#Язык C++

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI30 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как работает copy elision и RVO (Return Value Optimization)?

Copy Elision — это оптимизация компилятора, которая исключает ненужные копирования объектов. RVO (Return Value Optimization) — частный случай copy elision для возвращаемых значений.

Copy Elision: базовая идея

Компилятор может опустить (elide) вызовы конструктора копирования или перемещения в определённых ситуациях, даже если это изменяет поведение программы:

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

Widget w = Widget();  // Без оптимизации: создаём, копируем
                      // С copy elision (C++17+ обязательно): только создание

RVO (Return Value Optimization)

RVO — это оптимизация возвращаемых значений. Компилятор помещает объект напрямую в место, где он нужен:

class Matrix {
public:
    Matrix() { std::cout << "Constructor\n"; }
    Matrix(const Matrix&) { std::cout << "Copy\n"; }
    Matrix(Matrix&&) { std::cout << "Move\n"; }
};

Matrix createMatrix() {
    Matrix m;
    return m;  // С RVO: создаём m напрямую в месте вызова
}

int main() {
    Matrix result = createMatrix();
    // Выведет только Constructor, без копирований
}

Named Return Value Optimization (NRVO)

Когда возвращается локальная переменная, компилятор оптимизирует:

std::vector<int> getVector() {
    std::vector<int> vec;
    vec.push_back(1);
    vec.push_back(2);
    return vec;  // NRVO опускает копирование
}

std::vector<int> result = getVector();
// Вектор создан один раз в месте вызова

Обязательное Copy Elision (C++17+)

В C++17 copy elision обязателен, даже если конструктор копирования удалён:

class NonCopyable {
public:
    NonCopyable() = default;
    NonCopyable(const NonCopyable&) = delete;
};

NonCopyable create() {
    return NonCopyable();  // Работает в C++17+
}

int main() {
    NonCopyable nc = create();  // Нет копирования
}

Когда copy elision НЕ происходит

std::string getError(bool critical) {
    if (critical) {
        return "Critical error";
    }
    return "Warning";
}

std::string msg = getError(true);
// Copy elision может НЕ произойти при условных возвратах

Практический пример

std::vector<int> slowVersion() {
    std::vector<int> result;
    for (int i = 0; i < 1000000; i++) {
        result.push_back(i);
    }
    return result;  // С RVO: создание один раз
}

std::vector<int> fast = slowVersion();

Move семантика vs Copy Elision

class Heavy {
public:
    Heavy(Heavy&&) { std::cout << "Move\n"; }
};

Heavy create() {
    Heavy h;
    return std::move(h);  // ПЛОХО! Отключает RVO
}

Heavy create2() {
    Heavy h;
    return h;  // ХОРОШО! RVO сработает
}

Практические правила

  • По умолчанию используй return по значению для RVO
  • Не применяй std::move в return, если хочешь копировать
  • RVO обычно срабатывает с локальными переменными
  • Условные возвраты могут не оптимизироваться

Итого

Copy Elision и RVO:

  • Исключают ненужные копирования — критично для производительности
  • Обязательны в C++17+ для определённых случаев
  • Работают лучше всего с return по значению
  • Требуют осторожности при написании конструкторов с побочными эффектами
Как работает copy elision и RVO (Return Value Optimization)? | PrepBro