Объясните разницу между lvalue и rvalue.
Комментарии (2)
Ответ сгенерирован нейросетью и может содержать ошибки
lvalue vs rvalue
lvalue (left value) — выражение с адресом в памяти, существует после выполнения выражения. Пример: переменные, массивы, разыменованные указатели.
rvalue (right value) — временное значение без стабильного адреса, существует только во время вычисления. Пример: литералы, результаты выражений, временные объекты.
Простой способ запомнить
Если можно взять адрес &x, это lvalue. Если нет — rvalue.
Примеры
int x = 10; // x — lvalue, 10 — rvalue
int y = x + 5; // x + 5 — rvalue (результат)
int& ref = x; // OK: привязываем lvalue
// int& ref = 42; // ERROR: нельзя привязать rvalue к lvalue reference
const int& ref = 42; // OK: const reference может привязать rvalue
Rvalue reference (&&) — C++11
Rvalue reference позволяет захватить и переместить временный объект:
int&& rref = 42; // OK: захватили rvalue
int&& rref2 = x; // ERROR: переменная — это lvalue
int&& rref3 = move(x); // OK: move превращает в rvalue
Move семантика — основное применение
class Buffer {
public:
// Копирующий конструктор (медленно)
Buffer(const Buffer& other) { /* копировать данные */ }
// Конструктор перемещения (быстро!)
Buffer(Buffer&& other) : data(other.data) {
other.data = nullptr; // Берём данные, зануляем исходный
}
};
Buffer b1(1000);
Buffer b2 = move(b1); // Перемещение, а не копирование
Практический пример: vector.push_back
vector<string> vec;
string s = "hello";
vec.push_back(s); // Копирование
vec.push_back(move(s)); // Перемещение
vec.push_back(string("hi")); // Автоматическое перемещение rvalue
std::move и std::forward
std::move — явное превращение lvalue в rvalue (для перемещения):
vector<int> v1 = {1, 2, 3};
vector<int> v2 = move(v1); // Переместили v1 в v2
// v1 теперь пуст!
std::forward — идеальная передача параметров в шаблонах (perfect forwarding):
template<typename T>
void wrapper(T&& arg) {
other_func(forward<T>(arg)); // Сохраняет категорию (lvalue или rvalue)
}
Когда это важно
- Производительность — избегаем ненужных копий
- Владение ресурсом — unique_ptr использует move
- Контейнеры — vector::push_back использует move для rvalue
- Оптимизация — RVO (Return Value Optimization)
Return Value Optimization (RVO)
class Object {};
Object create() {
return Object(); // Может быть без копирования/перемещения!
}
Object obj = create(); // RVO: ноль операций!
Практический совет
- Используйте
unique_ptr<T>для явного владения (move semantics) - Используйте
const T&для чтения без копирования - Используйте
T&&в конструкторах перемещения - Используйте
std::move()для передачи владения
Правило большого пальца
// ❌ Копирование где не нужно
void process(string s) // Копирует s
// ✅ Правильно для временных значений
void process(string&& s) // Rvalue reference
// ✅ Правильно для чтения
void process(const string& s) // Const reference
Итог: lvalue/rvalue и move семантика — это одна из ключевых фишек C++11+, критична для производительности и безопасности памяти в современном C++.