프로그래밍/C++

오른값(rvalue)과 이동 대입 연산자

우대비 2022. 11. 9. 20:06
반응형

value는 두개로 나뉠 수 있는데 

하나는 왼값( lvalue ) 또 하나는 오른값( rvalue )

int a = 5;

위 코드에서 a가 왼값이고 5가 오른값이라고 할 수 있음

 

즉 왼값은

- 단일식을 넘어서 계속 지속되는 개체를 뜻함

 

오른값

- 왼값이 아닌 나머지로 임시 값, 열거형, 람다, i++ 등이 오른 값에 해당함

 

void TestKnight_Copy(Knight knight) {	}
void TestKnight_LValueRef(Knight& knight) {	} 
void TestKnight_ConstLValueRef(const Knight& knight) {	}

// &&를 넣으면 오른값을 넣을 수 있음 대신 왼값은 못넣음
void TestKnight_RValueRef(Knight&& knight) {   }
TestKnight_Copy(k1);
TestKnight_LValueRef(*k1);

// Knight()는 함수가 끝나면 없어지는데  멤버변수의 값을 바꾸는 불필요한 행동을 방지하기 위해 막혀있음
TestKnight_LValueRef(Knight()); // 오류

// const를 붙여주면 맴버변수를 건드리지 못하니까 허용은 됨
TestKnight_ConstLValueRef(Knight()); 
TestKnight_RValueRef(Knight());

보통 객체를 오른값으로 생성해서 인자로 보내는건 불가능하지만

&&를 붙여주면 오른값을 정상적으로 보내는게 가능함

 

 

그렇다면 이걸 어디다 쓸까!?

// 이동 대입 연산자
void operator=(Knight&& knight) noexcept
{
    cout << "operator=(Knight&& knight)" << endl;

    _hp = knight._hp;
    _pet = knight._pet;

    knight._pet = nullptr;
}

Knight k2;
k2._pet = new Pet();
k2._hp = 1000;

// 원본은 날려도 된다는  Hint로 생각하면 됨
Knight k3;
k3 = static_cast<Knight&&>(k2);

기본적으로 이동 대입 연산자에서 많이 사용되는데

lvalue인 k2를 ravlue로 캐스팅하고 k3에 대입 연산을 하면

Knight&& knight를 인자로 받는 이동 대입 연산자에 들어감

즉 원본은 날려도 된다고 암시하는게 됨 ( 그 이상의 의미는 크게 없다고 봐도 무방 )

 

이러한 이동 대입 연산자를 사용하는게 바로

std::move임

k3 = std::move(k2); // 오른값 참조로 캐스팅

사실 이러한 오른값 캐스팅 방법을 이용한 이동 대입 연산은

실제로 데이터가 이동되어서 원래 있던 자리에 데이터가 삭제되는건 아니기 때문에

특수한 상황이 아니면 사용하면 안됨

 

특수한 상황의 예

std::unique_ptr<Knight> uptr = std::make_unique<Knight>();
std::unique_ptr<Knight> upp = uptr; // 오류
std::unique_ptr<Knight> upp = std::move(uptr);

unique_ptr 단 하나만 존재할 수 있는 포인터라고 할 수 있음 때문에

같은 주소값을 들고 있는 객체가 있으면 안되기에 대입 연산이 막혀있음

이럴때 이동 대입 연산을 이용하면 깔끔하게 ptr의 이동이 가능함

반응형
LIST

'프로그래밍 > C++' 카테고리의 다른 글

C++ Lambda  (0) 2022.11.10
전달 참조(보편 참조)와 forward  (0) 2022.11.09
using, typedef  (0) 2022.11.09
중괄호 초기화와 initializer_list  (0) 2022.11.08
STL algorithm  (0) 2022.11.08