lvalue와 rvalue

Value Category

  • 정체를 알 수 있는가
    • 해당 식이 다른 식과 같은 것인지 아닌지 구분할 수 있는가?
    • 변수 - 주소값을 통해 구분
    • 함수 - 이름으로 구분
  • 이동 시킬 수 있는가
    • 해당 식을 안전하게 다른 곳으로 이동할 수 있는가?
    • 이동 생성자, 이동 대입 연산자를 사용할 수 있는지의 여부
      이동 가능 이동 불가능
    정체를 알 수 있다 xvalue lvalue
    정체를 알 수 없다 prvalue 쓸모없음

lvalue

  • 좌측값은 어떠한 표현식의 왼쪽, 오른쪽 모두에 올 수 있음
  • 이동(move)시킬 수 없음
  • 이름을 가진 대부분의 객체들은 모두 lvalue
    • 객체의 주소값을 취할 수 있기 때문
int a;
a;

int&& x = a; // error
  • 주소값 연산자(&) 를 통해 해당 식의 주소값을 알아 낼 수 있다
    • &++i&std::endl 은 모두 올바른 작업
    • 또한 lvalue 들은 좌측값 레퍼런스를 초기화 하는데에 사용할 수 있다
  • 주의할 점
void f(int&& a) {
  a;  // lvalue다
}

f(3);

// 식 a의 타입은 우측값 레퍼런스
// 식 a의 값 카테고리는 lvalue -> 이름이 있기 때문
// 따라서, 아래의 식은 컴파일이 가능함

// main.c
#include <iostream>

void f(int&& a) { std::cout << &a; } // a의 주소값 추적 가능
int main() { f(3); }
  • lvalue의 범주
    1. 변수, 함수의 이름, 어떤 타입의 데이터 멤버
        // ex)
        int i;
        void func1();
        std::endl;
      
    2. 좌측값 레퍼런스를 리턴하는 함수의 호출식
        // ex)
        std::cout << i;
        ++it;
      
    3. 복합 대입 연산자식
        // ex)
        a = b; a+=b; a*=b;
      
    4. 전위 증감 연산자식
        // ex)
        ++a; --a;
      
    5. 클래스의 멤버 메서드/변수를 참조할 때 (단, static 메서드도, enum도 아닌 경우)
        // ex)
       class A {
         int f();         // static 이 아닌 멤버 함수
         static int g();  // static 인 멤버 함수
       }
      
       A a;
       a.g;  // <-- lvalue
       a.f;  // <-- lvalue 아님 (아래 나올 prvalue)
      
    6. 배열 참조식
        // ex)
        int a[n];
      
    7. 문자 리터럴
        // ex)
        "hi"
      
      • 문자 리터럴의 케이스는 조금 특별하다
      • 문자열은 character의 const static 배열이며, 프로그램의 메모리 상에 존재한다
      • 분류상 glvalue에 속한다
      • xvalues는 될 수 없기에, lvalues로 분류된다

prvalue

  • 이동시킬 수 있음
  • 주소값을 취할 수 없음
  • 우측값은 식의 오른쪽에만, read-only (식의 좌측에 올 수 없음)
  • Take the variables themselves, not copying them
  • prvalue의 범주
    1. 문자열을 제외한 리터럴들
        // ex)
        42, true, nullptr;
      
    2. 레퍼런스가 아닌 것을 리턴하는 함수의 호출식
        // ex)
        str.substr(1,2);
        str1 + str2;
      
    3. 후위 증감 연산자식
        // ex)
        a++, b--;
      
    4. 산술 연산자식, 논리 연산자식
        // ex)
        // 오버로딩된 연산자 아닌 디폴트 연산자
        a + b, a && b, a < b;
      
    5. 주소값 연산자식
        // ex)
        &a;
      
    6. 클래스의 멤버 메서드/변수를 참조할 때 (단, m이 enum이거나 static 메서드인 경우)
    7. this
    8. enum 값
    9. 익명함수 (람다식)
        // ex)
        []() { return 0;};
      

xvalue

  • 좌측값처럼 주소값을 트래킹할 수 있지만, 또한 이동도 할 수 있는 카테고리
  • std::move()를 이용해서 우측값 레퍼런스를 리턴하는 호출식
    std::move(x);
    
    vector<int> a, b = {1,2,3,4,5};
    a = b; // a: 1,2,3,4,5 b:1,2,3,4,5
    a = std::move(b); // b-> rvalue, a:1,2,3,4,5 b:{}
    

출처

Categories: ,

Updated: