C++ — надзвичайно потужна мова програмування, але не без своїх особливостей. Якщо ви коли-небудь чули терміни lvalue та rvalue і думали, що вони звучать заплутано, ви не один. Не хвилюйтеся, цей пост допоможе розібрати ці концепції на зрозумілі шматочки, разом із практичними прикладами, щоб ви могли підвищити свої навички в сучасному C++! 💡
std::move та std::forward в C++ | автор: Cengizhan Varlı | Medium
📌 Що таке lvalue та rvalue в C++?
У C++ lvalue та rvalue описують природу виразів:
- lvalue (locator value): Позначає пам'ятну локацію з ім'ям (наприклад, змінна). Має адресу і може існувати поза поточним виразом.
- rvalue (right-hand value): Тимчасове значення, яке не має постійної пам'ятної локації. Зазвичай з'являється на правій стороні присвоєння.
Приклади:
int a = 10; // 'a' — lvalue, '10' — rvalue
int b = a; // 'b' — lvalue, 'a' — lvalue
int c = a + b; // 'a + b' — rvalue
🧩 Основна думка:
Якщо це може бути на лівій стороні присвоєння, це lvalue. Якщо ні — це rvalue.
🔗 lvalue та rvalue посилання
Сучасний C++ дозволяє явно посилатися як на lvalue, так і на rvalue.
- lvalue посилання (
T&
): Посилається на lvalue. - rvalue посилання (
T&&
): Посилається на rvalue.
Приклад посилань на lvalue та rvalue:
int x = 42;
// lvalue посилання
int& lref = x; // OK: lvalue посилання на x
// rvalue посилання
int&& rref = 42; // OK: rvalue посилання на тимчасове значення (42)
// Помилка: Не можна прив'язати lvalue посилання до rvalue
// int& invalid = 42;
🔧 Rvalue посилання у функціях
rvalue посилання особливо корисні для роботи з тимчасовими об'єктами. Давайте подивимось, як створити і використовувати rvalue посилання.
Код: Функція з Rvalue посиланням
#include
void processValue(int&& rvalue) {
std::cout << "Обробка rvalue: " << rvalue << "\n";
}
int main() {
processValue(42); // Передаємо rvalue
return 0;
}
Вивід:
Обробка rvalue: 42
Передача lvalue:
int main() {
int x = 42;
// processValue(x); // Помилка: Не можна прив'язати lvalue до rvalue посилання
return 0;
}
🧩 Основна думка:
Rvalue посилання приймають тільки rvalue. Але що, якщо ви хочете передати lvalue? Ось тут і приходить на допомогу std::move
.
🚀 Використання std::move
: Переміщення lvalue в rvalue
Функція std::move
в C++ перетворює lvalue на rvalue, дозволяючи викликати функції, які приймають rvalue посилання.
Код: Використання std::move
#include
#include // для std::move
void processValue(int&& rvalue) {
std::cout << "Обробка rvalue: " << rvalue << "\n";
}
int main() {
int x = 42;
processValue(std::move(x)); // Перетворення x (lvalue) на rvalue
std::cout << "Значення x після move: " << x << "\n";
return 0;
}
Вивід:
Обробка rvalue: 42
Значення x після move: 42
🔎 Чому x
не змінюється після move? std::move
просто перетворює; він не здійснює фактичного переміщення даних. Переміщення даних залежить від реалізації функції. У більш складних сценаріях (наприклад, з об'єктами) lvalue, що було переміщено, часто залишається в "валідному, але непередбачуваному" стані.
🌟 Наслідки використання std::move
- Ефективність: Дозволяє передавати володіння ресурсами без глибоких копій.
- Гнучкість: Дозволяє застосовувати логіку для rvalue до lvalue.
3.
Увага: Після переміщення не покладайтеся на стан оригінального об'єкта, якщо це не визначено явно.
🔧 Практичний приклад: Переміщення рядка
Ось типовий приклад використання std::move
з об'єктами, такими як std::string
:
#include
#include
#include
void processString(std::string&& str) {
std::cout << "Обробка рядка: " << str << "\n";
}
int main() {
std::string text = "Hello, World!";
processString(std::move(text)); // Переміщаємо text в rvalue
std::cout << "Значення text після move: " << text << "\n";
return 0;
}
Вивід:
Обробка рядка: Hello, World!
Значення text після move:
🧩 Основна думка:
Після переміщення text
перебуває у валідному, але непередбачуваному стані. Уникайте використання об'єктів, з яких здійснено переміщення, якщо вони не були явно повторно заповнені.
🚀 Підсумок
Розуміння lvalue, rvalue, rvalue посилань та std::move
є важливим для освоєння сучасного програмування на C++. Ці можливості дозволяють ефективно керувати ресурсами і є необхідними для роботи з такими просунутими концепціями, як семантика переміщення та ідеальне перенаправлення.
автор: Malinda Gamage
Перекладено з: 🔍 Modern C++: Understanding lvalues, rvalues, and std::move 🚀