코딩하는 두식이
Lvalue Rvalue std::move 본문
Lvalue와 Rvalue의 구분
C++에서 모든 표현식은 Lvalue 또는 Rvalue 입니다. Lvalue는 단일 표현식 이후에도 없어지지 않고 지속되는 객체입니다. 쉽게 생각해서 이름을 가지는 객체는 Lvalue라고 얘기할 수 있죠. 그러므로 const 타입을 포함한 모든 변수는 Lvalue 입니다. 반면에 Rvalue는 표현식이 종료된 이후에는 더이상 존재하지 않는 임시적인 값입니다. 상수 또는 임시 객체는 Rvalue 라고 얘기할 수 있겠네요. 그럼 이해를 돕기 위해서 예제를 통해 Lvalue와 Rvalue를 구분해 보도록 하겠습니다.
#include <string>
using namespace std;
int main()
{
int x = 3;
const int y = x;
int z = x + y;
int* p = &x;
cout << string("one");
++x;
x++;
}
Rvalue를 구분하기 쉽게 굵은 밑줄로 표시하였는데 조금 구분이 되시나요? x, y, z, p 등의 이름을 가지는 변수는 모두 Lvalue 이지만 상수값 3, 임시객체 string("one") 은 표현식이 종료되면 더이상 참조할 수 없는 값이기 때문에 Rvalue 입니다. x + y, &x 와 같은 표현식도 마찬가지이죠. 또 한가지 흥미로운 점은 ++x는 Lvalue인 반면에 x++은 RValue라는 점입니다. 둘 다 증가된 값을 리턴하지만 ++x는 증가된 x 자신을 리턴하기 때문에 Lvalue인 반면에 x++은 증가된 복사본을 리턴하기 때문에 Rvalue 이죠. 이렇게해도 Lvalue와 Rvalue가 잘 구분이 안되신다면 좀 더 확실하게 구분하는 방법이 있습니다. 바로 표현식에 주소 연산자 &를 붙여보는 건데요, & 연산자는 Lvalue를 요구하기 때문에 표현식이 Rvalue라면 컴파일 오류가 나게됩니다.
&(x++); // error C2102: '&' requires l-value
Rvalue 참조자 &&
지금까지 C++에서 int& a = b; 형태로 사용하였던 참조자(Reference)는 Lvalue 참조자입니다. C++11 표준에서는 Lvalue 참조자 이외에도 Rvalue를 참조할 수 있는 Rvalue 참조자가 추가되었습니다. 이름에서 어느정도 예상할 수 있듯이 Lvalue 참조자는 Lvalue만 참조할 수 있고 Rvalue 참조자는 Rvalue만 참조할 수 있습니다. Rvalue 참조자는 Visual Studio 2010 이상 버전의 컴파일러에서 사용 가능합니다.
{
return 10;
}
int main()
{
int lvalue = 10;
int& a = lvalue;
int& b = rvalue(); // error C2440: 'initializing' : cannot convert from 'int' to 'int &'
int&& c = lvalue; // error C2440: 'initializing' : cannot convert from 'int' to 'int &&'
int&& d = rvalue();
}
std::move 를 사용해서 성능을 개선해보자
std::move 는 이름 때문에 부르는 것과 동시에 어떤 이동 작업이 이뤄질 것 같지만, 실제로 Lvalue 를 Rvalue 로 casting 해줄 뿐이다.
(std::string 은 실제 작은 문자열은 stack memory 에 저장하고 큰 문자열은 heap memory 에 저장하지만, 설명에선 편의를 위해 항상 heap 에 저장한다고 가정한다.)
위 코드에서 B = A 수행 시 copy 가 일어난다. (copy assignment operator)
C = std::move(A) 수행 시 move 가 일어난다. (move assignment operator)
B = A 를 할 때는 copy 하기 때문에 B.data 는 당연히 “aaa” 가 복사되고 A.data 도 여전히 “aaa”를 가지고 있지만
C = std::move(A) 를 하면 move 하기 때문에 A.data 는 C.data 로 이동이 되어서 A.data 는 빈 문자열이 되고 C.data 는 “aaa” 를 가진다. 얻을 수 있는 이점은, 새로 메모리를 할당(malloc)하지 않아도 되고 이미 메모리에 할당된 것을 소유권만 C 에게 넘겨주기 때문에 copy 동작보다 빠르다. (move 로 할당한 이후로는 A를 사용하지 못하므로 주의)
'공부 > C++' 카테고리의 다른 글
스마트 포인터 (0) | 2022.06.28 |
---|---|
Effective C++ 항목 4 : 객체를 사용하기 전에 반드시 그 객체를 초기화하자 (0) | 2022.05.07 |
Effective C++ 항목 9 : 객체 생성 및 소멸 과정 중에는 절대로 가상 함수를 호출하지 말자 (0) | 2021.05.10 |
Effective C++ 항목 7 : 다형성을 가진 기본 클래스에서는 소멸자를 반드시 가상 소멸자로 선언하자. (0) | 2021.05.10 |
Effective C++ 항목 3 : 낌새만 보이면 const를 들이대 보자! (0) | 2021.05.10 |