좌측값 : 메모리 주소를 가지는 객체로 프로그램의 다른 위치에서 참조하거나 수정할 수 있는 값입니다.
ex) int x = 10;에서 'x'나 int w = x + y;에서 'w', 'x', 'y'는 좌측값입니다.
우측값 : 일시적인 값으로, 메모리 주소를 가지지 않으며 대개 함수 반환값이나 리터럴 값입니다.
ex) int x = 10;에서 '10'이나 int w = x + y;에서 'x + y'가 우측값입니다.
좌측값 참조와 우측값 참조
int x = 5, int y = 10;
int & l = x; // Lvalue Refernce
int&& r1 = 5; // Rvalue Reference
int&& r2 = x + y; // Rvalue Reference
&& 연산자를 사용하여 우측 값(Rvalue)을 참조하는 방식입니다.
우측값 참조 도입으로 해결하고자 한 문제점
이전 C++에서는 객체를 전달하거나 반환할 때, 복사 생성자를 통해 데이터를 복사해야 했습니다. 이 과정은 큰 데이터 구조에서 비용이 많이 들었고, 복사가 반복되면 성능이 급격히 저하되었습니다.
또한, 임시 객체를 사용하려면 항상 복사가 필요했는데, 임시 객체는 값이 금방 사라지기 때문에 복사 비용이 불필요하게 발생했습니다.
이와 같은 복사 비용 문제를 해결하고 임시 객체를 효율적으로 처리하기 위해 우측값 참조가 도입되었습니다. 이를 통해 복사 없이 리소스를 이동할 수 있게 되어, 성능을 최적화하고 불필요한 메모리 할당을 피할 수 있게 되었습니다.
유니버셜 참조는 템플릿에서 사용되는 T&&로, r-value와 l-value를 모두 처리할 수 있습니다.
가벼운 예시
template<typename T>
void foo1(T&& arg) { } // arg는 유니버셜 참조입니다.
void foo2(int&& num) { } // num은 우측값 참조입니다.
객체를 복사하지 않고 자원의 소유권을 이동시키는 방식입니다. 객체가 더 이상 필요하지 않은 자원을 다른 객체로 이동할 수 있게 해 줍니다. 이를 통해 불필요한 복사를 피하고, 성능을 크게 향상시킬 수 있습니다.
둘 모두 std::move 함수를 통해 구현됩니다.
인자의 값 카테고리(lvalue 또는 rvalue)를 손실 없이 그대로 전달하는 것입니다.
우측값으로 전달된 인자는 다른 함수에서 우측값으로 그대로 전달되어야 하며, 좌측값으로 전달된 인자는 다른 함수에 좌측값으로 전달되어야 합니다.
std::forward 함수는 유니버셜 참조로 전달된 인자에 대해 값 카테고리를 그대로 유지하면서 다른 함수에 전달할 수 있도록 도와주는 함수입니다. 이 함수는 주로 템플릿 함수에서 인자의 값을 전달할 때 사용됩니다.