오른값 참조(rvalue reference)

lvalue(왼값) : 단일식을 넘어서 계속 지속되는 개체
rvalue(오른값) : lavlue가 아닌 나머지 (임시 값, 열거형, 람다, i++ 등)

class Pet
{

};

class Knight
{
	public:
		Knight()
		{
			cout << "Knight()" << endl;
		}
		Knight(const Kngiht& knight)
		{
			cout << "const Knight&" << endl;
		}
		~Knight()
		{
			if (_pet)
				delete _pet;
		}

		// 이동 생성자
		Knight(Knight&& knight)
		{

		}

		void operator=(const Knight& knight)
		{
			cout << "operator=(const Knight&)" << endl;
			_hp = knight._hp;
			if (knight._pet)
				_pet = new Pet(*knight._pet);
		}

		// 이동 대입 연산자
		void operator=(Knight&& knight) noexcept
		{
			cout << "operator(Knight&&)" << endl;
			_hp = knight._hp;
			_pet = knight._pet;
			knight._pet = nullptr;
		}
	public:
		int _hp = 100;
		Pet* _pet = nullptr;
};

void TestKnight_Copy(Knight knight) {}
void TestKnight_LValueRef(Knight& knight) {} 
void TestKnight_ConstLValueRef(const Knight& knight) {}
void TestKnight_RValueRef(Knight&& knight) {} // 이동 대상

int main()
{
	Knight k1;
	TestKnight_Copy(k1);
	TestKnight_LValueRef(k1);
	TestKnight_LValueRef(Knight()); // Knight()로 생성된 객체는 Lvalue가 아니므로 불가능
	TestKnight_ConstLValueRef(Knight()); // const가 붙은 rvalue는 가능

	TestKnight_RValueRef(k1); // lvalue는 불가능
	TestKnight_RvalueRef(Knight()); // rvalue는 가능
	TestKnight_RvalueRef(static_cast<Knight&&>(k1)); 

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

	Knight k3;
	k3 = static_cast<Kngiht&&>(k2);
	k3 = std::move(k2); // 오른값 참조로 캐스팅, 이름 후보중 하나가 rvalue_cast

	std::unique_ptr<Knight> uptr = std::make_unique<Knight>();
	std::unique_ptr<Knight> uptr2 = std::move(uptr);

	return 0;
}

rvalue로 참조할시 원본 데이터가 유지되지 않아도 됨
rvalue로 참조하는 것은 원본은 날려도 된다는 힌트를 주는 쪽에 가까움
따라서 이동시 얕은 복사도 사용 가능


전달 참조(forwarding reference)

원래는 보편 참조(universal reference) 라는 이름을 가지고있었음

class Knight
{
	public:
		Knight() { cout << "기본 생성자" << endl; }
		Knight(const Knight&) { cout << "복사 생성자" << endl; }
		Knight(Knight&&) noexcept { cout << "이동 생성자" << endl; }
}

void TestRValueRef(Knight&& k)
{
}

void Test_Copy(Knight k)
{
}

template<typename T>
void Test_ForwardingRef(T&& param)
{
	// 왼값 참조일 경우 복사
	// 오른값 참조일 경우 이동
	Test_Copy(std::forward<T>(param));
}

int main()
{
	Knight k1;
	Test_RvalueRef(std::move(k1)); // rvalue_cast

	test_ForwardingRef(k1); // lvalue
	test_ForwardingRef(std::move(k1)); // rvalue

	auto&& k2 = k1; // lvalue
	auto&& k3 = std::move(k1); // rvalue

	Knight k;
	Knight& k4 = k; // lvalue
	Knight&& k5 = std::move(k); // rvalue
	Test_RvalueRef(k5); // 오른값 참조를 참조 - 불가능
	Test_RvalueRef(std::move(k5)); // 오른값 참조 - 가능
}

전달 참조는 형식 연역(type deduction)이 일어날때만 발생
lvalue를 넣어주면 lvalue 참조, rvalue를 넣어주면 rvalue 참조함
함수의 케이스를 줄일 수 있어 편리함
단, 이 때 lvaluervalue 둘 중 어느 것을 받더라도 같은 동작을 해야함

오른값 : 왼값이 아님 = 단일식에서 벗어나면 사용할 수 없음
오른값 참조 : 오른값만 참조할 수 있는 참조 타입