leesangwon0114

I am Research Engineer. Currently working in KT.

C++Intermediate 20. rvalue reference#1

14 Nov 2018 » c++

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


lvalue vs rvalue

lvalue

  • 등호(=)의 왼쪽과 오른쪽에 모두 놓일 수 있다.
  • 이름(id)를 가진다.
  • 단일 문장을 벗어나서 사용될 수 있다.
  • 주소 연산자로 주소를 구할 수 있다.
  • 참조를 리턴하는 함수

rvalue

  • 등호(=)의 오른쪽에만 놓일 수 있다.
  • 이름(id)이 없다.
  • 단일 문장에서만 사용된다.
  • 주소 연산자로 주소를 구할 수 없다.
  • 값을 리턴하는 함수. 임시객체. 정수/실수형 리터럴
#include <iostream>
using namespace std;

int x = 10;
int f1() {return x;}
int& f2() {return x;}

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

    v1 = 20; // v1: lvalue, 20: rvalue
    20 = v1; // error
    v2 = v1;

    int* p1 = &v1; // ok.
    int* p2 = &10; // error.

    f1() = 10; // error.
    f2() = 20; // ok.

    const int c = 10;
    c = 20;

    Point().x = 10; // error
    Point().set(10, 20); // ok. 나중에 temporary proxy에 사용됨(임시객체의 값을 변경)
}

c는 rvalue? lvalue? -> lvalue(이름이 있고, 주소도 구할 수 있음), 수정불가능한 lvalue 다.

rvalue는 모두 상수인 것은 아니다.


연산자와 lvalue

int operator++(int& a, int)
{
    int temp = a;
    a = a + 1;
    return temp;
}
// 전위형 증가 연산자 - 참조리턴
int& operator++(int& a)
{
    a = a + 1;
    return a;
}

int main()
{
    int n = 0;
    n = 10;

    n++ = 20; // operator++(n, int) error.
    ++n = 20; // operator++(n) ok

    ++(++n);

    int a = 10, int b = 0;

    a + b = 20; // error.

    int x[3] = {1,2,3};
    x[0] = 5; // x.operator[](0) = 5; lvalue
}

int main()
{
    int n = 0;
    int* p = &n;

    decltype(n) n1; // int
    decltype(p) d1; // int*
    decltype(*p) d2; // int ? int & -> *p = 20 int&

    int x[3] = {1,2,3};
    decltype(x[0]) d3; // int&
    auto a1 = x[0]; // int

    decltype(++n) d4; // int&
    decltype(n++) d5; // int
}

괄호안에 값이 왼쪽에 올 수 있으면 참조 없으면 값

auto는 참조 속성 없애니까 int 만 남음


rvalue reference

int main()
{
    int n = 10;

    int& r1 = n; //ok
    int& r2 = 10; // error

    const int& r3 = n; //ok
    const int& r4 = 10; // ok

    const Point& r = Point(1, 1);

    규칙 3.
    int&& r5 = n; // error
    int&& r6 = 10; // ok
}

일반참조로는 rvalue를 가르킬 수 없지만 const 는 가르킬 수 있음

C++ 규칙

  • 규칙 1. not const reference 는 lvalue 만 참조 가능
  • 규칙 2. const reference 는 lvalue 와 rvalue를 모두 참조 가능
  • 규칙 3. rvalue reference 는 rvalue 만 가리킬 수 있다.

const int& r4 = 10; 은 모든 rvalue가 상수인 것은 아니라 문제될 수 있음(Point에는 임시객체에서 상수가 아니였는데 r을 바꾸려고 시도하면 에러 발생)

-> 상수성이 추가되어 원본을 정확히 가르킨 것이 아님

규칙 3이 나오면서 & 하나는 lvalue reference 라 부름

새롭게 나온 && 를 rvalue reference 라 부름

lvalue는 lvalue 만 rvalue는 rvalue 만 가르키지만 const lvalue reference 는 lvalue와 rvalue를 모두 참조 가능하다가 핵심

원본에 상수성이 추가되지 않은 상태로 순수하게 원본을 가르키고 싶은 rvalue가 C++11 문법에 추가됨

move 와 perfect forwarding 만들 때 필요함


reference 와 함수 오버로딩

#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번, 1번이 없으면 2번
    foo(10); // 3번, 3번이 없으면 2번

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

    int&& r2 = 10; // 10은 rvalue, 10을 가르키는 이름있는 r2라는 참조변수는 lvalue 이다.
    foo(r2); // lvalue 이므로 1번 호출됨

    foo(static_cast<int&&>(r2)); // 3
}

rvalue를 가르키는 이름이 있는 참조 변수는 lvalue 이다