rvalue, lvalue 관련

MwG·2025년 3월 21일

rvalue는 일반적으로 "임시 값", "이름 없는 값"으로
즉, 한 번 쓰고 버리는 값이라고 생각하면 된다.

그렇기에 lvalue 레퍼런스를 하듯 rvalue 레퍼런스를 사용할 경우 오류가 발생할 수 있다. 따라서 &&라는 기호를 사용하여 rvalue 레퍼런스를 사용할 수 있다.

왜 사용하는 것일까?

불필요한 복사를 피해서 성능(메모리, 속도...)을 크게 향상시키기 위해서이다.

복사를 할 경우 깊은 복사는 비용이 매우 크다. (메모리 할당, 데이터 복제 등)
이동은 그냥 포인터만 옮기면 되기 때문이다. (자원 뺏기)

관련 함수 및 추가 정보..

std::move함수

lvalue를 rvalue로 형 변환 해주는 역할 -> 이동 연산자 (&& a)를 사용하는데 쓰임.
우측값 레퍼런스를 할 때 쓰인다.

#include <utility>  // for std::move
#include <iostream>

template<typename T>
class tt
{
	int a;

public:
	// 기본 생성자
	tt() : a(0) {}

	// 복사 생성자
	tt(const tt& t)
	{
		a = t.a;
		std::cout << "복사 생성자" << std::endl;
	}

	// 이동 생성자
	tt(tt&& t)
	{
		a = t.a; // int는 move 의미 없지만 일단 값 복사
		std::cout << "이동 생성자" << std::endl;
	}
};

int main()
{
	tt<int> t;                      // 기본 생성자
	tt<int> t2(std::move(t));       // 이동 생성자
}

std::forward<>

✅ std::forward란?
"넘겨받은 값을 원래의 속성(lvalue인지 rvalue인지) 그대로 다음 함수에 전달하는 것"

이걸 perfect forwarding (완벽 전달)이라고 한다.

🔍 왜 필요한가?
📦 일반적인 템플릿 함수에서

template <typename T>
void wrapper(T arg) {
    callee(arg); // 문제 발생 가능
}

여기서 arg는 항상 좌측값(lvalue)로 취급됨
원래 rvalue였던 값도 lvalue로 바뀜!
즉, 원래 이렇게 부르면:

wrapper(std::string("hi")); // <- 이건 rvalue!
그런데 callee(arg)로 넘기면 arg는 이름 있는 변수 → lvalue
→ 그래서 callee는 복사 생성자를 호출하게 됨 ❌

✅ 해결법: std::forward

template <typename T>
void wrapper(T&& arg) {
    callee(std::forward<T>(arg)); // 💥 여기서 원래 성질 유지됨!
}

여기서 T&&는 universal reference (또는 forwarding reference)
std::forward(arg)는
T가 lvalue면 → lvalue로 전달
T가 rvalue면 → rvalue로 전달
즉, 전달받은 값의 원래 성격(lvalue/rvalue)을 그대로 전달하는 게 핵심!

rvalue 레퍼런스 관련 MS 문서

0개의 댓글