3 분 소요

1. rvalue

  • lvalue:
    • ”=” 을 기준으로 왼쪽과 오른쪽에 모두 올 수 있음
    • 이름이 있음, 단일식을 넘어서 메모리에 존재함
    • “참조”를 리턴하는 함수
    • 주소연산자로 주소를 구할 수 있음
  • rvalue:
    • ”=” 을 기준으로 오른쪽에만 올 수 있음
    • 이름이 없음, 단일식에서만 사용
    • “값”을 리턴하는 함수
    • 주소연산자로 주소를 구할 수 없음
    • 임시객체, literal

2. 코드로 알아보기

#include <iostream> 
using namespace std; 
  
int x = 10; 
int  foo() { return x; } 
int& goo() { return x; } 

class Point {
public:
  int x, y;
  Point(int x, int y) : x(x), y(y) {}
  void set(int x, int y) {
      x = x;
      y = y;
  }
};

int main() 
{ 
  int v1 = 10; 
  int v2 = 20; 

  v1 = 10; // v1 : lvalue, 10 : rvalue 
  10 = v2; // error 
  v2 = v1; // ok

  foo() = 20; // error, 값반환은 임시객체이기 때문에 lvalue가 될 수 없음
  goo() = 20; // ok

  cout << &v1 << endl;
  cout << &10 << endl; // error 

  const int c = 10; 
  c = 20; // c는 수정 불가능한 lvalue 

  // rvalue는 상수인가? 답은 No, 단지 왼쪽에 올 수 없을 뿐임
  Point(1, 2).x = 10; // error 
  Point(1, 2).set(10, 20); //ok 
}
#include <iostream> 
using namespace std; 
  
#define PRINT_L_R_VALUE(...)                                       \ 
if (is_lvalue_reference<decltype((__VA_ARGS__))>::value)           \ 
    printf(#__VA_ARGS__ " : lvalue\n");                            \ 
else if (is_rvalue_reference<decltype((__VA_ARGS__))>::value)      \ 
    printf(#__VA_ARGS__ " : rvalue(xvalue)\n");                    \ 
else                                                               \ 
    printf(#__VA_ARGS__ " : rvalue(prvalue)\n"); 

int main() 
{ 
  int n = 0; 
  n = 10; // ok, n은 lvalue 
  //n + 1 = 20; // error, n+1은 rvalue 

  ++n = 20; // ok, 메모리, lvalue 
  // n++ = 20; // error, 이전 값, rvalue 

  PRINT_L_R_VALUE(n);   // lvalue
  PRINT_L_R_VALUE(n++); // rvalue
  PRINT_L_R_VALUE(++n); // lvalue

  const int c = 0; 
  PRINT_L_R_VALUE(c);   // lvalue
}

2.1. rvalue와 참조 문법

  • non const lvalue reference(일반 참조)는 lvalue만 참조 가능
  • const lvalue reference(상수 참조)는 lvalue, rvalue 모두 참조 가능
  • rvalue reference는 rvalue만 참조 가능
#include <iostream>

class Point {
public:
  int x, y;
  Point(int x, int y) : x(x), y(y) {}
  void set(int x, int y) {
    x = x;
    y = y;
  }
};

int main() 
{ 
  int n = 10; 

  // 1. non const lvalue reference(일반 참조)는 lvalue만 참조
  int& r1 = n;  // ok 
  int& r2 = 10; // error 

  // 2. const lvalue reference는 lvalue 와 rvalue 모두 참조
  const int& r3 = n;  // ok 
  const int& r4 = 10; // ok 

  // 3. rvalue reference는 rvalue 만 참조
  int&& r5 = n;  // error 
  int&& r6 = 10; // ok 
  Point&& r = Point(0, 0); // 임시객체 
}

2.2. 참조와 함수 오버로딩

#include <iostream> 
using namespace std; 
  
// 참조와 함수 오버로딩 
void foo(int& a)       { cout << "int&" << endl; }       // 1 
void foo(const int& a) { cout << "const int&" << endl; } // 2 
void foo(int&& a)      { cout << "int&&" << endl; }      // 3 
  
int main() 
{ 
  int n = 10; 

  foo(n);  // 1번 호출, 없으면 2번 
  foo(10); // 3번 호출, 없으면 2번 

  int& r1 = n; 
  foo(r1); // 1번 호출, 없으면 2번 


  int&& r2 = 10; // 10은 rvalue지만, r2는 이름있는 변수이므로 lvalue입니다. 
                  // "named rvalue reference is lvalue" 

  // 중요!. r2는 lvalue, 주소도 있고 이름도 있음 
  foo(r2); // 1번 호출, 없으면 2번 
  foo(static_cast<int&&>(r2)); // 3번 호출 
}
typedef int& LR; 
typedef int&& RR; 
  
int main() 
{ 
  int n = 10; 

  LR r1 = n;    // ok 
  RR r2 = 10;   // ok 

  // reference collapsing 이라고 함, 결론적으로 && &&의 경우만 &&
  LR& r3 = n;   // int& & -> int& 
  RR& r4 = n;   // int&& & -> int& 

  LR&& r5 = n;  // int& && -> int& 
  RR&& r6 = 10; // int&& && -> int&& 

  // 주의 직접 레퍼런스의 레퍼런스를 만들수는 없음
  // typedef 나 템플릿에서만 허용됩니다. 
  int& & r7 = n; // error 
} 

참고

codenuri 강석민 강사 강의 내용기반으로 정리한 내용입니다.
코드누리


This is personal diary for study documents.
Please comment if I'm wrong or missing something else 😄. 

Top

태그: , ,

카테고리:

업데이트:

댓글남기기