← Назад к вопросам
Как работает 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 по значению
- Требуют осторожности при написании конструкторов с побочными эффектами