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

Объясните разницу между lvalue и rvalue.

2.0 Middle🔥 152 комментариев
#ООП и проектирование#Структуры данных и алгоритмы#Язык C++

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

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

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

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)
}

Когда это важно

  1. Производительность — избегаем ненужных копий
  2. Владение ресурсом — unique_ptr использует move
  3. Контейнеры — vector::push_back использует move для rvalue
  4. Оптимизация — 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++.