##L-Value와 R-Value
L-Value Reference(좌측값 참조)와 R-Value Reference(우측값 참조)에 관한 간단한 정리
- 좌측값 참조 ○ 이름이 있고 계속 남아있는 값 을 L-Value(좌측값)이라고 한다 ○ 좌측값 참조는 좌측값만을 참조한다 ○ ‘&’ (레퍼런스 연산자)를 이용함, 즉 대입받은 값을 참조한다 ○ 임시값은 좌측값 참조를 할수 없다 ○ L-value 참조 생성자/할당자 연산은 복사연산이라 한다 => 연산 후에도 매개인자가 남아있다
</br>
- 우측값 참조 ○ 임시값, 해당 줄에서만 남아있는 값을 R-Value(우측값)이라고 한다 ○ 우측값 참조는 우측값만을 참조한다 ○ ‘&&’ 연사자를 이용함 ○ R-value 참조 생성자/할당자 연산은 이동연산이라 한다 => 연산 후에 매개인자가 남아있지 않는다(임시값이기 때문) ○ 우측값이 임시 객체여서 대입 이후 소멸할 경우, 컴파일러가 이동대입 연산자를 적용하여 객체 내용을 이동시킴으로써 오버헤드 방지(불필요한 복사를 하지 않는다)
일반적인 코드에서 본다면
int num = 10; // num: 좌측값, 10이라는 값이 저장되어 해당 줄이 지나고도 남는것
// 10: 우측값, num에 대입되고 나서 해당 줄이 지나면 사라지는 값
이다.
CodingGom이 C++11과 STL을 처음 공부할때 처음 조사하게 되었는데, R-Value 의도입과 이동 의미론의 등장으로 성능향상을 이룩했다라는 내용이 기억난다.
하지만, 이때 조사를 하면서 느꼈던 의문점이 있는데… R-Value Reference를 이용한 이동연산… “이게 정말로 진짜 이동 연산인가? 그냥 좀 특이한 복사 연산이 아닌가?”
그렇다면 난 왜 저런 의문을 가지게 되었을까? 일단 우측값 참조의 형태를 보자
Type&& refRvalue = R-Value;
R-Value를 참조하고 있는 refRvalue는 L-Value 이다. 왜? 저 줄이 지나도 남아 있으니까! 음… R-Value를 참조하고 있으니까 R-Value로써 사용하고자 하는데 L-Value 라니… 이 의문에 대한 해결은 std::move() 와 std::forward()를 통한 R-Value 캐스팅이다.
좌측값인 우측값 참조를 우측값으로 사용하기 위해서는 좌측값 속성을 우측값 속성으로
두 함수에 대한 간단한 설명은 다음과 같다.
-
두 함수 모두 인자로 넘어오는 값을 R-value로 캐스팅 시켜주는 함수 템플릿 ○ std::move() : 무조건 R-value로 캐스팅 ○ std::forward() : 조건에 맞을경우 R-value로 캐스팅 ○ 캐스팅을 시행할뿐 실제로 이동을 시키지는 않는다 ○ 실행시점에서는 아무일도 발생하지 않는다 ○ 이동할 객체(인자로 넘겨줄 객체)는 const 여서는 안된다 => const일 경우 이동이 아닌 복사가 일어난다 ○ 작동 방식(코드)
template < typename T > decltype(auto) move(T&& param) { using returnType = std::remove_reference_t<T>&&; return static_cast<returnType>(param); }
위 설명중 ‘실제로 이동을 시키지 않는다’ 는 부분을 보면 이동은 직접 구현해야 한다. 이동을 구현하지 않으면 복사연산일 뿐이잖아? 라는 것이 의문이었던거 같다.
결론, 이동연산을 쓸 수 있는 부분은 기능을 구현해서 성능을 향상시키자라는 의미가 아닐까 싶다.