본문 바로가기
개발/C++ (98,03,11,14,17,20,23)

Modern C++ : lvalue, rvalue, value category

by snowoods 2025. 8. 17.

Modern C++

Lvalue, Rvalue, and C++ Value Category

 

1. 개요

C++11의 핵심 기능인 Rvalue 참조(Rvalue references)이동 의미론(Move Semantics)을 이해하려면 Lvalue와 Rvalue의 개념을 명확히 알아야 합니다. 이 문서는 전통적인 Lvalue/Rvalue 개념부터 C++11 이후 확장된 값 카테고리(value categories)까지, 기존의 유용한 예제들을 바탕으로 종합적으로 설명합니다.

 

2. Lvalue와 Rvalue의 기본 개념

가장 간단한 구분 기준은 "표현식의 주소를 안전하게 얻을 수 있는가?" 입니다.

  • Lvalue (Locator Value): 식별 가능한(identifiable) 메모리 위치를 가지는 표현식입니다. 이름이 붙어 있거나, 포인터로 참조할 수 있어 표현식이 끝난 후에도 객체가 살아남습니다.
  • Rvalue (Read Value): 식별 가능한 메모리 위치를 가지지 않는 임시적인(temporary) 값입니다. 표현식이 끝나면 바로 소멸됩니다.
int i = 3;  // i는 lvalue, 3은 rvalue

int j = i;  // i와 j 모두 lvalue. 
            // 단, j에 i의 '값'을 대입하기 위해 i는 lvalue-to-rvalue 변환을 거침

 

3. Lvalue/Rvalue 특징과 상세 예제

Lvalue가 되는 경우

  1. 전위 증가/감소 연산자 (++i, --i)
    • 연산 후 객체 자신(lvalue)을 반환합니다.
      ```cpp
      int nCount = 0;
    • +nCount = 5; // 유효. nCount의 값이 5가 됨
  2. 간접 참조(역참조) 연산자 (*p)
    • 포인터가 가리키는 메모리 위치(lvalue)를 반환합니다.
      ```cpp
      int a[10];
      int *p = a;
    • p = 3; // 유효
    • (p + 1) = 4; // 유효. (p+1)은 rvalue이지만, 역참조한 결과 * (p+1)은 lvalue
  3. 참조를 반환하는 함수 호출
    • 함수가 반환하는 참조는 특정 객체(lvalue)를 가리킵니다.
      int i, j;
      int& get_bigger(int& a, int& b) {
        return (a > b ? a : b);
      }
      get_bigger(i, j) = 10;  // 유효. i와 j 중 더 큰 객체에 10을 대입

Rvalue가 되는 경우

  1. 리터럴 (Literals)
    • 3, 3.14, 'a', true 등 값 자체는 rvalue입니다.
      // 3 = i; // 오류: 리터럴은 대입의 대상이 될 수 없음
  2. 열거형 상수 (Enum constants)
    • 열거형의 멤버 또한 rvalue로 취급됩니다.
      enum Color { red, green, blue };
      // blue = green; // 오류: 열거형 상수는 rvalue
  3. 대부분의 이항 연산자 결과
    • a + b, a - b 등의 연산 결과는 새로운 임시 값(rvalue)입니다.
      int m = 1, n = 2;
      // (m + 1) = n;  // 오류: (m+1)의 결과는 rvalue
  4. 후위 증가/감소 연산자 (i++, i--)
    • 연산 이전의 값을 복사한 임시 객체(rvalue)를 반환합니다.
      int nCount = 0;
      nCount++ = 5;  // 오류: nCount++의 결과는 rvalue
  5. 주소 연산자 (&)의 결과
    • 주소 '값' 자체는 rvalue입니다.
      int n, *p;
      p = &n;     // 유효. &n의 결과를 p에 대입
      // &n = p;     // 오류. &n의 결과(주소 값)는 rvalue
  6. 값을 반환하는 함수 호출
    • 함수가 값으로 반환한 객체는 임시 객체(rvalue)입니다.
      char* fun() { return "Hello"; } // "Hello"는 수정 불가능한 메모리에 존재
      char* q = fun();
      // q[0] = 'h';  // 런타임 오류. 문자열 리터럴을 수정하려 함

4. C++11 값 카테고리: prvalue, xvalue

C++11부터는 값의 특성을 더 명확히 하기 위해 카테고리를 세분화했습니다.

      표현식 (expression)
      /       \
   glvalue     rvalue
   /   \       /   \
 lvalue xvalue  prvalue
  • prvalue (pure rvalue): 순수 rvalue. 리터럴, 임시 객체 등 전통적인 rvalue 대부분이 해당됩니다.
  • xvalue (eXpiring value): "곧 소멸될 값". 자원을 이동시킬 수 있는 lvalue처럼 취급됩니다. std::move의 결과가 대표적인 xvalue입니다.
  • glvalue (generalized lvalue): lvalue + xvalue. 정체성(identity)이 있는 표현식.
  • rvalue: prvalue + xvalue. 이동이 가능한(movable) 표현식.

 

5. std::move와 이동 의미론

std::move는 lvalue를 xvalue로 캐스팅하는 함수입니다. 이 캐스팅을 통해 컴파일러는 해당 객체를 "곧 사라질 것이니 자원을 훔쳐가도 좋다"고 인식하게 됩니다.

std::string s1 = "hello";
std::string s2 = std::move(s1); // 1. std::move(s1)이 s1을 xvalue로 캐스팅
                                // 2. s2는 이동 생성자(std::string(std::string&&))를 호출
                                // 3. s1의 내부 포인터를 s2로 이동(자원 절도)
                                // 4. s1은 비어있는 "유효하지만 명시되지 않은 상태"가 됨

std::move 자체는 아무것도 옮기지 않습니다. 단지 rvalue 참조를 받는 이동 생성자이동 대입 연산자가 호출되도록 유도할 뿐입니다.

 

6. 참조 바인딩 규칙

  • Lvalue 참조 (&): Lvalue만 바인딩할 수 있습니다.
  • const Lvalue 참조 (const &): Lvalue와 Rvalue 모두 바인딩할 수 있어 매우 유용합니다.
  • Rvalue 참조 (&&): Rvalue(prvalue, xvalue)만 바인딩할 수 있습니다.
std::string s = "text";

std::string& ref1 = s;            // OK: Lvalue -> Lvalue 참조
// std::string& ref2 = "text";   // 오류: Rvalue -> Lvalue 참조

const std::string& ref3 = s;      // OK: Lvalue -> const Lvalue 참조
const std::string& ref4 = "text"; // OK: Rvalue -> const Lvalue 참조

// std::string&& ref5 = s;         // 오류: Lvalue -> Rvalue 참조
std::string&& ref6 = std::move(s);  // OK: xvalue(Rvalue) -> Rvalue 참조

중요: 이름이 부여된 Rvalue 참조 변수 자체는 Lvalue입니다. (주소를 가질 수 있으므로)

 

7. 요약

카테고리 정체성(주소) 이동 가능 대표 예시
Lvalue O X 변수, *p, ++x
prvalue X O 리터럴, x+y, x++
xvalue O O std::move(x)

 

8. 참고 자료

  1. cppreference - Value categories