본문 바로가기

스터디/C++

R-value, L-value -> Value-type

C에서 R-value 는

오른쪽에 있는 값. 

L-value 는

왼쪽에 있는 값이다.


int a=3;


일 경우 R-value는 3, L-value 는 a 가 된다.


C++에서는 R-value는 사라지는 값

   L-value는 사라지지 않는 값.



l value - 고유하면서 움직일 수 없는 값

x value - 고유하면서 움직일 수 있는 값

pr value - 순수한 값

gl value - 다른 조건 없이 고유하기만 하면 됨



l-value <-- gl-value --> xvalue


xvalue <-- r-value --> pr value


이런식으로 그림이 그려진다.




int x-3;

const int y = x;

int z = x+y;

int* p = &x;


cout << string("Hello");


++x;

x++;


여기서 R-value 는

진하게 한것이다.


x++; 가 R 밸류인 이유는 x 가 연산후에 값이 증가시키므로 기존 x 값에 대한 연산후에 증가한다.

따라서 기존 x 값을 저장할 임시 객체가 생성되고, 사라지므로 x++; 는 R-value 이다.



Q. Rvalue reference 는 Lvalue 일까 Rvalue 일까?

A. Lvalue (Rvalue =/= Rvalue Reference)



- 코드 곳곳에서 발생하는 불필요한 Rvalue 복사과정, 이로 인한 오버헤드


Q. 아래의 코드에서 오버헤드는 총 몇번 발생할까?


std::string a,b = "Hello ", c = "world";

a=b+c;




std::string appendString(std::string param);

std;:string result = appendString("Hello")




std::string a,b = "Hello ", c = "world";

a=b+c;



std::string appendString(std::string param);

std;:string result = appendString("Hello")


총 3번 발생한다.


임시객체를 이용한 불필요한 연산때문에 낭비가 일어난다.



이를 막으려고 Move Semantics 라는게 있다.


Move Semantics 란? 

문자열의 직접적인 복사가 아닌, 포인터의 이동으로 낭비를 막는것.


A-1. Move 생성자, Move 대입 연산자를 구현후 사용.

A-2. Rvalue Reference를 파라미터로 받는 함수 사용.



--> 이렇게 할 경우, 컨테이너에 객체를 삽입할 때 더이상 포인터를 넣지 않아도 됨.

--> vector 컨테이너와 같은 대규모 리소스를 반환하는 함수 작성 가능.



Q. std::move 함수가 수행하는 일은?

A. 파라미터를 "무조건" Rvalue Reference로 타입 캐스팅


--> 절대 이름에 낚이면 안된다.

std::move 호출만으로는 아무것도 아동시키지 않는다.



std::move 함수는

Lvalue를 Rvalue로 취급하고 싶을 떄 사용한다.

(컴파일러에게 해당 객체가 이동해도 무관한 객체임을 알려주기 위해서)



1. void foo(string&& param);

2. string&& var = string();

3. auto&& var2 = var1;

4. template<typename T>

   void foo(T&& param);


위에서 R-value 를 찾아보자.

답은 1,2 번이다.


&& 참조자가 템플릿 함수의 템플릿 파라미터 또는 auto 타입과 함께 사용되었을 경우

Universal Reference 라고 한다.

(R-value 아님!!!!)




Universal Reference 란?

- Lvalue와 Rvalue를 모두 참조할 수 있는 포괄적 (Universal) 레퍼런스

- 반드시 Rvalue Reference와 구분되어야함

- Perfect Forwarding 구현을 위한 열쇠





Old C++ 에서는 해결할 수 없는 문제가 하나 있었는데,

그건 바로 Forwading Problem 이었다.


- make_shared 함수와 같은 factory 함수 작성


template<typename T, typename Arg>

T* factory(arg& arg)

{

return new T(arg);

}


struct Y

{

Y(const int& n) {}

};


--> 결국 non-const 와 구분하며 모두 명시해 주어야함.


- 작성해야 하는 함수의 개수가 2^n 으로 증가한다. (n=파라미터 갯수)

- 가변인자는 말할 것도 없음.


따라서 universal 이 필요함.


int n = 0;

x* px = factory<X>(n);

y* px = factory<Y>(10);


이렇게 짧게 줄일 수 있다.