l-value
왼값
r-value
오른값
오른값 참조
아마 직접적으로 오른값 참조를 쓸 일은 없겠다만 &&
이 무엇인지 알아야 한다
벡터의 이사 방식이 이동이다. 경우에 따라 이동과 복사의 차이가 엄청날 수 있기 때문이다.
class Knight()
{
public:
// 복사 대입 연산자
void operator=(const Knight& knight)
{
_hp = knight._hp;
if (knight._pet)
_pet = new Pet(*knight._pet); // 깊은 복사
}
// 이동 생성자
Knight(Knight&& knight) noexcept
{
_hp = knight._hp;
_pet = knight._pet;
knight._pet = nullptr;
}
// 이동 대입 연산자 ... 그냥 "소유권을 이전했다" 라고 생각해도 좋다. 복사한게 아니라.
void operator=(Knight&& knight) noexcept
{
_hp = knight._hp;
_pet = knight._pet;
knight._pet = nullptr; // 어차피 사라질 애니까 nullptr 로 밀어주기
}
public:
int _hp = 0;
Pet* _pet = nullptr;
};
void TestKnight_LValueRef(Knight& knight)
{
// 원본 넘겨줄테니... 건드려도 됨
}
void TestKnight_ConstLValueRef(const Knight& knight)
{
// 원본 넘겨줄테니... 건드릴 순 없어
}
void TestKnight_RValueRef(Knight&& knight) // 오른값 참조라는 뜻
{
// 원본 넘겨줄테니... 더 이상 활용하지 않을테니 맘대로 해!
// 일회성이니 원본 더 안쓸거니 알아서 해
}
int main()
{
Knight k1;
k1._pet = new Pet();
Knight k2;
// k2 = static_cast<Knight&&>(k1); // 이동~~~ 츕츕!
k2 = std::move(k1); // 위 문장과 완전 똑같은 의미이다
TestKnight_ConstLValueRef(Knight()); // 임시객체 넘기기
// TestKnight_RValueRef(Knight());
TestKnight_RValueRef(static_cast<Knight&&>(k1)); // 오른값 참조
}
보편 참조 (universal reference) 와 똑같은 아이이고 역사가 있는 아이인데 전달 참조라고 부른다. (C++17부터)
오른값 참조랑 비슷하게 생겼다. 근데&&
가 붙는다고 무조건 오른값 참조가 아니다!
⭐️ template
, auto
처럼 '형식 연역(type deduction)' 이 등장할 때에만 전달 참조가 발생한다. 왼값을 넣으면 왼값 참조, 오른값을 넣으면 오른값 참조로 동작한다는 특징을 가진다.단 const 를 붙여주면 바로 오른값 참조로 바뀐다.
class Knight
{
public:
Knight() {cout << "기본 생성자" << endl;}
Knight(const Knight&) {cout << "복사 생성자" << endl;}
Knight(Knight&&) noexcept {cout << "이동 생성자" << endl;}
};
void Test_RValueRef(Knight&& k) // 오른값만 참조할 수 있다
{
}
template<typename T>
void Test_ForwardingRef(T&& param) // 전달 참조 // param 도 결국 왼값이라 복사 생성자가 호출된다
{
// 왼값 참조라면 복사, 오른값 참조라면 이동
Test_Copy(std::forward<T>(param));
/*
// 왼값 참조라면 복사
TestCopy(param);
// 오른값 참조라면 이동
Test_Copy(std::move(param)); // 이렇게 오른값 참조로 다시 바꿔줘야 한다.
*/
}
int main()
{
Knight k1; // 왼값
// Test_RValueRef(k1); // error
Test_RValueRef(std::move(k1)); // ok // rvalue cast
Test_ForwardingRef(k1); // &
Test_ForwardingRef(std::move(k1)); // &&
auto&& k2 = k1; // &
auto&& k3 = std::move(k1); // &&
return 0;
}
int main()
{
Knight& k4 = k1; // 왼값 참조
Knight&& k5 = std::move(k1); // 오른값 참조
// Test_RValueRef(k5); // error // k5 가 단일식을 벗어나도 사용 가능하므로
Test_RValueRef(std::move(k5)); // ok 👌
}