[1일 1블로그 무사고 6일] C++ L-value와 R-value에 대해서
L-value와 R-value
C++11 이전까지 lvalue와 rvalue는 이름과 마찬가지로 표현식에서 왼쪽에 올 수 있는 값과 오른쪽에 존재하는 값으로 구분이 되어 있었다.
현재까지도 쓸 수 있는 개념이지만 C++11 이후 추가로 설명이 필요하다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
int main()
{
// Left, Right, Result는 모두 Lvalue로서 식별자를 가짐
// 다른 데이터를 복사해서 자신의 상태를 변경할 수 있다
// 3, 7, 0은 모두 Rvalue
int Left = 3;
int Right = 7;
int Result = 0;
// Rvalue는 다른 데이터의 메모리를 복사할 공간이 없거나, 식별자가 따로 존재하지 않기에 대입이 불가능하다
3 = Right; // error C2106 : '='의 왼쪽 피연산자는 lvalue이어야 한다
Left + Right = 10; // error C2106 : '='의 왼쪽 피연산자는 lvalue이어야 한다
Func() = 20; // error C2106 : '='의 왼쪽 피연산자는 lvalue이어야 한다
}
Lvalue
기본적으로 Lvalue는 식별자를 가지고 다른 값을 복사받을 수 있기에 대입이 가능하지만
Rvalue에 해당하는 값들은 상수거나 따로 식별자가 존재하지 않기에 자신의 상태를 수정할 수 없다
C++11 이전에는 lvalue와 rvalue가 말 그대로 표현식의 왼쪽, 오른쪽에 올 수 있는 값들을 의미했는데
이후에는 lvalue는 존재하는 메모리를 식별할 수 있는지도 포함하게 되었고 const의 등장으로 메모리의 위치를 식별할 수 있더라도 수정할 수 없을 수도 있게 되었다.
메모리의 영역을 가지고 주소도 가지지만 수정 불가능한 lvalue의 종류
- 배열 타입
- 불완전한 타입(큰 의미X)
- const 한정자가 붙은 타입
Rvalue
rvalue는 메모리의 위치와 식별자를 특정할 수 없는 데이터들을 의미함
위에서 Left + Right의 2개의 int를 연산한 결과를 보통 rvalue라고 부르는데 식을 계산한 후의 값임
이때 연산의 결과인 10의 메모리는 분명 존재하지만, 그 메모리의 위치를 알아내야 할 식별자가 존재하지 않음
int와 int를 plus할 때 필요한 메모리는? 12바이트다, 왜냐 int(4) + int(4)를 통해 나온 결과 값 int(4)가 합쳐져 12바이트가 필요함
C++11 이후부터 lvalue와 rvalue 레퍼런스에 대한 세부적인 형 변환 함수와 레퍼런스를 지원하게 된 이유
lvalue 레퍼런스는 C++에서 레퍼런스를 처음 접할 때 배우는 것으로 일반적으로 자료형&을 통해 선언되며 다른 lvalue를 통해 초기화 시키지 않으면 사용할 수가 없다
rvalue 레퍼런스는 마치 2중 포인터처럼 보이며 자료형&&를 통해 선언되며 일반적으로 lvalue 레퍼런스에 대입되지 않는 값들을 참조할 수 있다
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int Func() { return 10; }
int main()
{
int TestInt = 0;
// Lvalue 레퍼런스
// 메모리가 확실히 존재하고 위치를 식별할 수 있는 변수들만 대입이 가능
int& LeftRef1 = TestInt;
int& LeftRef2 = Func();
// Rvalue 레퍼런스
// 메모리 위치가 명확한 lvalue에 대한 참조가 불가능함
// 메모리의 위치를 특정할 수 없는 rvalue에 대한 참조를 받을 수 있다
int&& RightRef1 = TestInt;
int&& RightRef2 = Func();
}
Rvalue 레퍼런스는 왜 생겨났고 어디에 쓰이나??
rvalue 레퍼런스는 move semantics을 위해 c++11에서 도입되었는데(move semantics : 객체의 리소스(동적 메모리 등)을 다른 객체로 이동하는 것)
c++11 이전에는 객체를 함수에 전달하거나 함수에서 반환할 때 불필요한 객체 복사본이 만들어지는 경우가 많아 성능이 비효율적이었음
rvalue 참조를 사용하면 불필요한 복사본을 생성할 필요 없이 한 개체에서 다른 개체로 리소스를 효율적으로 전송할 수 있음
이 것을 자세~~히 설명해둔 블로그가 있으니 옮기는 것보다 그 블로그로 가서 자세히 정독하는게 더 좋을 것 같아 이정도까지하고 물러나겠다