Effective C++/Effective Modern C++
[Effective Modern C++] 24. 보편 참조와 rValue
김디트
2025. 3. 11. 11:11
728x90
항목 24. 보편 참조와 rValue를 구별하라
T&& 라는 표현
- 반드시 rValue 참조가 아니다.
void f(Widget&& param); // rValue
Widget&& var1 = Widget(); // rValue
auto&& var2 = var1; // rValue 아님
template<typename T>
void f(std::vector<T>&& param); // rValue
template<typename T>
void f(T&& param); // rValue 아님
- 두 가지 의미
- rValue 참조
- 보편 참조(universal reference)
- rValue 참조 혹은 lValue 참조 둘 중 하나라는 뜻
- 때에 따라서 lValue처럼 행동할 때도 있다.
- 거의 모든 것에 묶을 수 있다.
보편 참조의 두 가지 문맥
- 함수 템플릿 매개변수
template<typename T>
void f(T&& param); // 보편 참조
- auto 선언
auto&& var2 = var1; // 보편 참조
타입 추론이 일어날 때는 보편 참조이다.
- 함수 템플릿에서는 param의 타입이 추론된다.
- var2의 선언에서는 var2의 타입이 추론된다.
- 타입 추론이 일어나지 않는다면 rValue 참조이다.
void f(Widget&& param); // rValue
Widget&& var1 = Widget(); // rValue
- 보편 참조는 참조이므로 반드시 초기화해야 한다.
template<typename T>
void f(T&& param); // 보편 참조
Widget w;
f(w); // lValue 전달. param의 타입은 Widget&(즉, lValue)
f(std::move(w)); // rValue 전달. param의 타입은 Widget&&(즉, rValue)
보편 참조이기 위해서는 반드시 T&&의 형태여야 한다.
- 아래의 경우는 rValue이다.
template<typename T>
void f(std::vector<T>&& param); // 타입 추론이 발생하지만 rValue
std::vector<int> v;
f(v); // 컴파일 에러. lValue를 넘길 수 없다.
- const 한정사 하나만 붙여도 참조는 보편 참조가 될 수 없다.
template<typename T>
void f(const T&& param); // 보편 참조가 아니라 rValue
템플릿 안에서 T&&를 발견한다고 해도 반드시 보편 참조는 아니다.
- 반드시 타입 추론이 일어난다는 보장이 없기 때문이다.
template<class T, class Allocator = allocator<T>>
class vector
{
public:
void push_back(T&& x); // 보편 참조가 아니다.
...
};
- vector 클래스를 사용한 인스턴스가 생성되면 그 시점에 타입이 고정되기 때문이다.
std::vector<Widget> v;
// 위의 경우 void push_back(Widget&& x); 로 함수는 고정되며
// 타입 추론이 발생할 여지가 없다.
- 반면 emplace_back은 보편 참조를 사용한다. 타입 추론이 발생하기 때문이다.
templace<class T, class Allocator = allocator<T>>
class vector
{
public:
template<calss.. Args>
void emplace_back(Args&&... args); // args는 보편 참조
...
};
auto&& 타입으로 선언된 변수는 보편 참조이다.
- 반드시 타입 추론이 발생하며
- 형태(T&&)가 정확하기 때문이다.
- C++14 람다 작성 시 많이 발견할 수 있다.
auto timeFucInvocation = [](auto&& func, auto&&... params)
{
타이머를 시작한다.
std::forward<decltype(func)>(func)( // 항목 33 참조
std::forward<decltype(params)>(params)...
);
타이머를 정지하고 경과 시간을 기록한다.
};
728x90