Rvalue 란 무엇인가?
Rvalue, 우측값은 대입 시에 항상 오른쪽에만 오는 식을 말한다.
예제로 이해하는 것이 쉽다.
1 | int doSomething() |
x, y 처럼 이름을 가지고 있고, 그 이름으로 접근할 수 있는 것이 Lvalue 이다.
그와 반대로 (x + y) 나 상수 10, 20 등은 이름을 가지고 있지 않고 식이 끝난 후 다음 라인에서 그 값에 접근할 수 없다.
Rvalue 를 함수의 매개변수로 받고 싶으면 A&& 로 선언한다.
참고로 doSomethingWithRvalue 안에서 rhs 를 다시 사용할 때는 rhs 가 Lvalue 라는 것을 주의하자. Rvalue 로 사용하고 싶으면 다시 std::move(rhs) 와 같이 사용해주어야 한다.
1 | class A; |
std::move 를 사용해서 성능을 개선해보자
std::move
는 이름 때문에 부르는 것과 동시에 어떤 이동 작업이 이뤄질 것 같지만, 실제로 Lvalue 를 Rvalue 로 casting 해줄 뿐이다.
1 | class myClass |
(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를 사용하지 못하므로 주의)
예제를 하나 더 보자.
1 | std::vector<std::string> stringList; |
std::string 을 원소로 가지는 vector stringList 가 선언되어 있고, input 을 push_back 해준다.
push_back 이 후에 input은 다시 쓰이지 않기 때문에 위 코드에서 input 은 vector에 string 을 넣기위해서만 만들어진 임시객체의 역할 밖에 되지 않는다. 이럴 때 std::move 를 사용하면 성능 향상이 될 수 있다.
1 | std::vector<std::string> stringList; |
그리고 예제에선 std::string 이 move constructor / move assignment operator 를 지원하기 때문에 move로 기대하는 동작을 얻을 수 있는 것이고, 사용자가 정의한 class 의 경우 destructors 나 assignment operators 를 명시적으로 선언했다면 move constructor 나 move assignment operators 가 암시적으로 생성되지 않으므로 직접 작성해주거나 default 로 선언해줘야 할 것이다.
암시적으로 생성되는 move constructors 의 형태는 아래와 같다. 기본적으로 클래스 멤버변수를 std::move 해서 Rvalue 로 casting 해 초기화한다.
1 | class myClass |