본문 바로가기
C,C++/정보정리

[C/C++] Lvalue, Rvalue

by 마두식 2023. 2. 10.
반응형
Lvalue, Rvalue
  • 복사 생략(Copy Elision)

-  컴파일러 자체에서 복사를 생략하는 작업을 복사 생략이라고 한다.

ex)  A a(A(2)); 와 같은 경우 A(2) 에 대한 일반 생성자를 호출한 후 생성된 임시 객체로 a가 복사 생성되는 게 정석이지만, 컴파일러가 이러한 과정을 생략해서 a 자체를 A(2)로 만들어진 객체로 해버리는 것이다.


-  함수 내부에서 생성된 객체를 그대로 리턴할 때 수행할 수 있다.

=>  C++ 표준을 봤을 때, 복사 생략을 해야한다 가 아니라, 복사 생략을 할 수도 있다 라고 표기되어 있다.
==>  즉, 경우에 따라 복사 생략을 해야하지만 복사 생략이 되지 않을 수도 있다.

 

  • 좌측값(lvalue)과 우측값(rvalue)

-  모든 C++ 표현식(expression)의 경우 두 가지 카테고리로 구분할 수 있다.

=>  어떤 타입을 갖는지, 어떤 종류의 '값'을 갖는지


-  좌측값이란, 주소값을 취할 수 있는 값을 의미한다.

ex)  int a = 3; 표현식에서 'a'는 메모리 상에서 존재하는 변수, 즉 주소값을 취할 수 있는 값이다. 따라서 a는 좌측값이다.
=>  좌측값이라고 해서 왼쪽에만 와야한다는 의미가 아니다.


-  우측값이란, 주소값을 취할 수 없는 값을 의미한다.

ex)  int a = 3; 표현식에서 3은 주소값을 취할 수 없다. 따라서 3은 우측값이다.
=>  우측값은 항상 식의 오른쪽에만 와야 한다.


-  지금까지 다룬 레퍼런스는 '좌측값'에만 레퍼런스를 가질 수 있다.

=>  & 하나를 이용해서 정의하는 레퍼런스를 좌측값 레퍼런스(lvalue reference)라고 부른다.
==>  좌측값 레퍼런스 자체도 좌측값이다.


-  & 하나를 이용하는 것은 좌측값 레퍼런스를 의미하지만, 예외적으로 const T& 타입에 한해서만, 우측값도 레퍼런스로 받을 수 있다.

=>  const 레퍼런스이기 때문에 임시로 존재하는 객체의 값을 참조만 할 뿐 이를 변경할 수 없기 때문에 가능하다.

 

  • 우측값 레퍼런스

-  우측값의 레퍼런스를 정의하기 위해서는 좌측값과는 달리 &를 두 개 사용해서 정의해야 한다.

=>  Array&& ary 라고 적혀있다면, Array 타입의 우측값을 받는 것이다. 여기서 ary 자체는 좌측값이다. 즉, ary는 타입이' Array의 우측값 레퍼런스'인 좌측값인 것이다.


-  우측값 레퍼런스의 경우 반드시 우측값의 레퍼런스만 가능하다.

-  우측값 레퍼런스는 레퍼런스 하는 임시 객체가 소멸되지 않도록 붙들고 있는다.

-  우측값 레퍼런스를 이용한 생성자를 이동 생성자라고 한다.

 

  • 이동 생성자 작성 시 주의할 점

-  C++의 컨테이너들은 이동 생성자에서 예외가 발생했을 때 이를 제대로 대처하기 어렵다.

 

  • Move 함수(move semantics)

-  좌측값을 복사가 아닌 이동을 하려면 좌측값이 우측값으로 취급될 수 있게 바꿔주면 된다.

=>  swap 함수를 생각해보면, 좌측값 두개를 교환할 때 불필요한 복사가 3번 발생한다. 이를 복사가 아닌 이동으로 해결하면 성능에서 이득을 볼 수 있다.
ex)  String 객체 2개를 swap 한다고 가정했을 때, 복사를 하면 배열 내의 원소 하나하나를 따로 옮겨주어야 하지만, 이동을 하면 배열의 주소값만 서로 바꿔준다.
복사에서 배열 내의 원소 하나하나를 따로 옮겨주는 이유는 swap 함수 내에서 임의 객체 tmp를 만들어서 사용하기 때문이다. tmp는 swap 함수가 종료되는 시점에서 소멸되므로 이동처럼 사용할 수 없다.


-  C++ 11 부터 <utility> 라이브러리에서 좌측값을 우측값으로 바꾸어주는 move 함수가 제공된다.

-  std::move 함수가는 인자로 받은 객체를 우측값으로 변환해서 리턴해준다.

=>  단순한 타입 변환만 수행한다.


-  C++ 컴파일러가 템플릿 타입을 추론할 때, 템플릿 인자 T가 레퍼런스가 아닌 일반적인 타입이라면 const를 무시한다.

 

  • 보편적 레퍼런스(Universal reference)

-  템플릿 인자 T에 대해서, 우측값 레퍼런스를 받는 형태를 보편적 레퍼런스라고 한다.

=>  보편적 레퍼런스는 우측값만 받는 레퍼런스와 다르다.


-  일반적인 타입의 우측값 레퍼런스는 우측값만을 인자로 받을 수 있지만 템플릿 타입의 우측값 레퍼런스는 우측값 뿐만 아니라 좌측값 역시 받아낼 수 있다.

=>  좌측값이 왔을 때는 C++ 11에서는 레퍼런스 겹침 규칙(reference collapsing rule)에 따라 T의 타입을 추론한다.

 

  • forward 함수

-  우측값 레퍼런스일때만 move를 적용한 것처럼 작동한다.

 

 

 


 

 

 

틀린 부분이나 이상한 부분이 있으면 댓글로 편하게 지적해주세요!

감사합니다!

 

참고

씹어먹는 C++ - <12 - 1. 우측값 레퍼런스와 이동 생성자> (modoocode.com)

씹어먹는 C++ - <12 - 2. Move 문법 (std::move semantics) 과 완벽한 전달 (perfect forwarding)> (modoocode.com)

반응형

댓글