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

Что такое rvalue?

1.8 Middle🔥 211 комментариев
#Linux и операционные системы#Qt и GUI

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

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

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

rvalue: Временные объекты и семантика перемещения

rvalue (правое значение) — это выражение, которое обозначает временный объект или объект без стабильного местоположения в памяти. Это фундаментальная концепция современного C++ (начиная с C++11), которая кардинально изменила управление памятью и производительность.

lvalue vs rvalue

lvalue (левое значение) — это выражение, которое имеет стабильный адрес в памяти:

int x = 42;           // x — lvalue (имеет адрес &x)
int& ref = x;         // ссылка на lvalue
int* ptr = &x;        // можно взять адрес

rvalue — это временный объект, который существует только в конце полной выражения:

42;                   // литерал 42 — rvalue
x + 1;                // результат + — rvalue
std::string("hello"); // временный объект — rvalue

Различие:

int x = 5;    // x — lvalue, 5 — rvalue
int& a = x;   // OK: можно привязать lvalue-ссылку к lvalue
int& b = 5;   // ОШИБКА: нельзя привязать к rvalue
const int& c = 5;  // OK: const ссылка может привязаться к rvalue

rvalue-ссылки (&&)

C++11 ввёл rvalue-ссылки (&&) — новый тип ссылок, которые привязываются только к rvalue:

int& x_ref = x;       // lvalue-ссылка
int&& x_rref = 5;     // rvalue-ссылка — OK
int&& x_rref2 = x;    // ОШИБКА: нельзя привязать к lvalue

Семантика перемещения (Move semantics)

Основная цель rvalue-ссылок — перемещение вместо копирования. Вместо дорогостоящей копии можно перенести ресурсы:

class String {
private:
    char* data;
    size_t length;

public:
    // Копирующий конструктор (медленно)
    String(const String& other) {
        length = other.length;
        data = new char[length];
        std::copy(other.data, other.data + length, data);
    }
    
    // Перемещающий конструктор (быстро)
    String(String&& other) noexcept {
        data = other.data;        // Просто копируем указатель!
        length = other.length;
        other.data = nullptr;     // Очищаем исходный объект
        other.length = 0;
    }
};

String create_string() {
    return String("hello");  // Rvalue — будет использован move
}

String s = create_string();  // Нет копирования, только перемещение!

Перемещающие операции

1. Перемещающий конструктор (move constructor):

MyClass(MyClass&& other) noexcept {
    // Переносим ресурсы
}

2. Перемещающий оператор присваивания (move assignment):

MyClass& operator=(MyClass&& other) noexcept {
    if (this != &other) {
        cleanup();           // Освобождаем старые ресурсы
        // Переносим ресурсы
    }
    return *this;
}

std::move: Явное приведение к rvalue

std::move превращает lvalue в rvalue-ссылку (без копирования):

std::vector<std::string> strings;
std::string s = "hello";

strings.push_back(s);           // Копирует s
strings.push_back(std::move(s)); // Перемещает s (s теперь пустая!)

Важно: std::move НЕ копирует и НЕ перемещает — это просто приведение типа! Реальное перемещение происходит в конструкторе/операторе присваивания.

Идеальная передача (Perfect forwarding)

template<typename T>
void wrapper(T&& arg) {
    // Если arg был lvalue, сохраняет как lvalue
    // Если arg был rvalue, сохраняет как rvalue
    process(std::forward<T>(arg));
}

MyClass obj;
wrapper(obj);                // T = MyClass&, передаём как lvalue
wrapper(MyClass());           // T = MyClass, передаём как rvalue

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

// До C++11: дорогое копирование
std::vector<int> v1(1000000);
std::vector<int> v2 = v1;  // Копирует 1 млн элементов

// C++11+: перемещение
std::vector<int> v3 = std::move(v1);  // v1 теперь пуста, O(1)

// Factory функции
std::unique_ptr<Data> create_data() {
    auto ptr = std::make_unique<Data>();
    return ptr;  // RVO или move, НЕ копирование
}

Когда компилятор использует rvalue

Автоматически:

  • Возврат временных объектов из функций
  • Результаты арифметических операций
  • Возврат локальных переменных (RVO/NRVO)

Явно через std::move:

  • Передача во владеющие контейнеры
  • Реализация swap
  • Оптимизация производительности

Правила написания move-aware кода

  1. Rule of Five — если определяешь destructor, copy-ctor, copy-operator, определи и move-ctor, move-operator
  2. noexcept — помечай move-операции как noexcept для оптимизаций
  3. Используй std::move в factory функциях и при передаче во владеющие контейнеры
  4. Не перемещай дважды без проверки

Резюме: rvalue и move semantics — это мощный механизм для оптимизации производительности, позволяющий избежать дорогостоящих копий и создать эффективные системы обработки больших объёмов данных.

Что такое rvalue? | PrepBro