0. value

C++에서 value는 크게 3가지로 분류된다.

분류 예시 설명
lvalue int a = 5;, a, str 메모리 주소를 가지는 객체
prvalue 42, "hello", std::string("hi") 임시 객체(pure rvalue)
xvalue std::move(a), std::string().substr(0,2) 곧 소멸될 객체(expiring value), 이동 가능 대상

xvalue는 lvalue도 아니고 prvalue도 아닌, 리소스를 뺏어와도 되는 값이다. 임시 객체가 아니지만, 자원을 안전하게 옮겨도 괜찮은 상태를 의미한다.

1. &(lvalue reference) vs &&(rvalue reference)

문법 의미 사용 목적
T& lvalue reference(좌측값 참조) 수정 가능한 참조, 기존에 존재하는 값에 접근
T&& rvalue reference(우측값 참조) 임시값이나 소멸 직전의 값에 참조하여 이동 가능하게 함

어떤 경우에 사용되는지 예시를 보자.

void foo(int& x)
{
	std::cout << "lvalue reference\n";
}

void foo(int&& x)
{
	std::cout << "rvalue reference\n";
}

int main()
{
	int a = 10;
	foo(a); //lvalue -> 출력 : lvalue reference
	foo(10); // rvalue -> 출력 : rvalue reference
	foo(std::move(a)) // xvalue -> 출력 : rvalue reference

	return 0;
}

2. std::move

std::move는 객체를 xvalue로 캐스팅하여 이동 생성자/이동 대입 연산자가 호출되도록 유도한다. 내부적으로 static_cast<T&&>를 수행한다.

예시를 보자.

#include <utility>
#include <string>

std::string a = "hello";
std::string b = a; // 복사
std::string c = std::move(a); // 이동
  • std::string b = a; : b가 새 메모리를 할당 후, a의 문자열을 복사한다. 만약 더 이상 a가 필요 없는 경우, 메모리를 할당하고 값을 복사하는 오버헤드가 발생한다.
  • std::string c = std::move(a) : c가 a의 내부 포인터만 훔쳐오기 때문에 메모리를 할당할 필요가 없다. a의 내부 포인터는 nullptr 또는 빈 문자열로 초기화된다.

즉, std::vector, std::string, 사용자 정의 클래스 등 내부에 동적 메모리를 가지는 경우에서, std::move를 사용하면 힙 메모리를 새로 할당/복사하지 않고 포인터와 사이즈 정보만 빠르게 옮기는 것이 가능하다.
이동 후 원본 객체는 “valid but unspecified state”, 즉 파괴해도 되지만 읽거나 쓰는 것은 피해야 하는 상태가 된다.

3. 이동 생성자(move constructor)

아래는 이동 생성자 예시 코드이다.

class Example
{
	Example(Example&& other)
	{
		mArr = other->mArr;
		other->mArr = nullptr;
	}

private:
	int* mArr;
}

other가 xvalue라면 새로운 인스턴스를 만들 때, mArr을 새로 할당하고 깊은 복사를 하는 것보다 other->mArr 포인터를 훔치는 것이 효율적이다. 인자로 rvalue reference가 들어오면 되는 것처럼 보이지만, 실제로 이동 생성자가 호출되려면 인자로 들어오는 표현식이 xvalue(ex. std::move(obj))여야 한다.

주의할 점

std::move는 이동을 가능하게 해주는 것일 뿐, 진짜 이동이 일어나는지는 상황에 따라 다르다.
만약 이동 생성자가 없고 복사 생성자만 있다면, std::move를 사용해도 결국 복사 생성자가 실행된다.
따라서 이동하려면 이동 생성자/연산자가 정의돼 있어야 한다. 또한 리소스를 다른 객체로 이동하고, 원래 객체를 더 이상 사용하지 않을 때 사용해야 한다.

Leave a comment