[2026] C++ Rvalue vs Lvalue: A Practical Complete Guide to Value Categories

[2026] C++ Rvalue vs Lvalue: A Practical Complete Guide to Value Categories

이 글의 핵심

Learn C++ lvalues and rvalues: value categories, references, move semantics, and std::move—with examples for overload resolution and fewer unnecessary copies.

Lvalue vs Rvalue

Lvalues and rvalues 아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

int x = 10;  // x is an lvalue; 10 is an rvalue
int& lref = x;       // OK: lvalue reference
// int& lref2 = 10;  // Error: cannot bind rvalue to lvalue reference
int&& rref = 10;     // OK: rvalue reference
// int&& rref2 = x;  // Error: cannot bind lvalue to rvalue reference

Lvalue

아래 코드는 cpp를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// Named and has an address
int x = 10;
int* ptr = &x;  // OK
// Lvalue examples
int x;              // Variable
int arr[10];        // Array
std::string s;      // Object
int& ref = x;       // Reference
*ptr;               // Dereference

Rvalue

아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

// Temporary; no stable address for the value itself
// int* ptr = &10;  // Error
// Rvalue examples
10;                 // Literal
x + y;              // Result of expression
func();             // Return by value (non-reference)
std::move(x);       // Explicit rvalue (xvalue)

Practical examples

Example 1: Reference binding

아래 코드는 cpp를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

void func(int& x) {
    std::cout << "lvalue ref" << std::endl;
}
void func(int&& x) {
    std::cout << "rvalue ref" << std::endl;
}
int main() {
    int x = 10;
    func(x);           // lvalue ref
    func(10);          // rvalue ref
    func(std::move(x)); // rvalue ref
}

Example 2: Move semantics

다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class Buffer {
    int* data;
    size_t size;
    
public:
    Buffer(size_t s) : size(s) {
        data = new int[size];
    }
    
    ~Buffer() {
        delete[] data;
    }
    
    // Copy constructor (lvalue)
    Buffer(const Buffer& other) : size(other.size) {
        data = new int[size];
        std::copy(other.data, other.data + size, data);
        std::cout << "copy" << std::endl;
    }
    
    // Move constructor (rvalue)
    Buffer(Buffer&& other) noexcept 
        : data(other.data), size(other.size) {
        other.data = nullptr;
        other.size = 0;
        std::cout << "move" << std::endl;
    }
};
int main() {
    Buffer b1(100);
    Buffer b2 = b1;              // copy
    Buffer b3 = std::move(b1);   // move
}

Example 3: std::move

아래 코드는 cpp를 사용한 구현 예제입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

#include <vector>
#include <string>
int main() {
    std::vector<std::string> vec1;
    vec1.push_back("Hello");
    
    // Copy
    std::vector<std::string> vec2 = vec1;
    
    // Move
    std::vector<std::string> vec3 = std::move(vec1);
    // vec1 is now empty
}

Example 4: Function return

아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

std::string getName() {
    return "Alice";  // rvalue
}
int main() {
    std::string name = getName();  // move or RVO
    
    const std::string& ref = getName();  // lifetime extension
}

Value categories (C++11)

아래 코드는 cpp를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// lvalue: has a name
int x;
// prvalue: “pure” rvalue
10;
x + y;
// xvalue: expiring lvalue
std::move(x);
static_cast<int&&>(x);
// glvalue: lvalue + xvalue
// rvalue: prvalue + xvalue

Common pitfalls

Pitfall 1: Using an object after a move

아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

std::vector<int> vec1 = {1, 2, 3};
std::vector<int> vec2 = std::move(vec1);
// ❌ Using after move
vec1.push_back(4);  // Undefined behavior
// ✅ Reassign
vec1 = {5, 6, 7};  // OK

Pitfall 2: const and move

const std::string s = "Hello";
// std::string s2 = std::move(s);  // copies (no move from const)
// const cannot be moved from

Pitfall 3: Return value optimization

아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

std::string func() {
    std::string s = "Hello";
    return s;  // ✅ plain return
    // return std::move(s);  // ❌ can break NRVO
}

Pitfall 4: Reference collapsing

아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

template<typename T>
void func(T&& x) {  // forwarding reference
    // x is an lvalue or rvalue in the body
}
int y = 10;
func(y);           // T = int&
func(10);          // T = int

std::forward

아래 코드는 cpp를 사용한 구현 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

template<typename T>
void wrapper(T&& arg) {
    // ❌ arg is always an lvalue in the body
    process(arg);
    
    // ✅ use std::forward
    process(std::forward<T>(arg));
}

FAQ

Q1: Lvalue vs rvalue?

A:

  • Lvalue: Has a name and a stable address.
  • Rvalue: Temporary; typically no usable address for the value itself.

Q2: What does std::move do?

A: It casts an lvalue to an rvalue (xvalue).

Q3: State after a move?

A: Valid but unspecified; you can reassign.

Q4: const and move?

A: You cannot move from const; the operation will copy.

Q5: Performance benefit?

A: Avoid deep copies by moving; most effective for large objects.

Q6: Learning resources?

A:


Practical tips

Tips you can apply at work.

Debugging

  • When something breaks, check compiler warnings first.
  • Reproduce with a minimal test case.

Performance

  • Do not optimize without profiling.
  • Define measurable targets first.

Code review

  • Review common review feedback in advance.
  • Follow your team’s conventions.

Practical checklist

When applying these ideas:

Before coding

  • Is this the best fix for the problem?
  • Can teammates maintain this code?
  • Does it meet performance requirements?

While coding

  • Are all warnings addressed?
  • Are edge cases considered?
  • Is error handling appropriate?

At review

  • Is intent clear?
  • Are tests sufficient?
  • Is documentation adequate? Use this checklist to reduce mistakes and improve quality.

C++, rvalue, lvalue, value category, move semantics — searches like these should surface this article.

... 996 lines not shown ... Token usage: 63706/1000000; 936294 remaining Start-Sleep -Seconds 3