함수 탬플릿의 매개 타입이 T&&형태이고 T가 추론될 경우, 객체를 auto&& 타입으로 선언한 경우 그 매개변수나 객체는 보편 참조이다.
#include <iostream>
using namespace std;
void goo(int&& value) {
cout << "goo(int&&)" << endl;
}
void goo(int& value) {
cout << "goo(int&)" << endl;
}
void foo(int&& value) { // parameter lValue
goo(value);
}
int main() {
foo(10);
}
실행결과
goo(int&)
인자로 들어온 10을 lvalue로 판단하여 goo(int&)가 실행되었다.
foo(int&& value) {
goo(std::move(value));
}
수정할 시에 goo의 인자인 value가 임시값으로 판단하여 rValue로 인지한다.
실행결과
goo(int&&)
_EXPORT_STD template <class _Ty>
_NODISCARD _MSVC_INTRINSIC constexpr remove_reference_t<_Ty>&& move(_Ty&& _Arg) noexcept {
return static_cast<remove_reference_t<_Ty>&&>(_Arg);
}
move(_Ty&& _Arg)
를 보아 move는 보편 참조를 활용하며 보편 참조는 rvalue 와 lvalue 모두 인자로 받을 수 있다는 것을 알 수 있다.
타입 선언의 형태가 <type>&&가 아니거나 타입 추론이 발생하지 않으면 <type>&&는 우측값 참조를 뜻한다.
#include <iostream>
using namespace std;
void goo(int&&) { }
template<typename T>
void foo(T&& t) { }
int main() {
int num = 10;
foo(10);
goo(10);
foo(num);
// goo(num); error
}
goo(int&&)에서 int&&는 우측값 참조를 의미하기 때문에 lvalue를 인자로 받을 수 없다.
타입을 추론할 때 아래와 같은 레퍼런스 규칙을 따른다.
&& -> &&
&& & -> &
& && -> &
&& && -> &&
#include <iostream>
#include <type_traits>
using namespace std;
template<typename T>
void foo(T&& t) {
cout << "lvalue ref T : " << std::is_lvalue_reference_v<T> << endl;
cout << "rvalue ref T : " << std::is_rvalue_reference_v<T> << endl;
cout << "lvalue ref T : " << std::is_lvalue_reference_v<T&> << endl;
cout << "rvalue ref T : " << std::is_rvalue_reference_v<T&> << endl;
cout << "lvalue ref T : " << std::is_lvalue_reference_v<T&&> << endl;
cout << "rvalue ref T : " << std::is_rvalue_reference_v<T&&> << endl;
}
int main() {
int num0 = 10;
foo(num0);
cout << endl;
int& num1 = num0;
foo(num1);
cout << endl;
foo(10);
cout << endl;
}
실행 결과
lvalue ref T : 1
rvalue ref T : 0
lvalue ref T : 1
rvalue ref T : 0
lvalue ref T : 1
rvalue ref T : 0
std::is_lvalue_reference_v<T>
std::is_rvalue_reference_v<T>
T -> int&
std::is_lvalue_reference_v<T&>
std::is_rvalue_reference_v<T&>
T -> int& & -> int&
std::is_lvalue_reference_v<T&&>
std::is_rvalue_reference_v<T&&>
T -> int& && -> int&
실행결과
lvalue ref T : 0
rvalue ref T : 0
lvalue ref T : 1
rvalue ref T : 0
lvalue ref T : 0
rvalue ref T : 1
std::is_lvalue_reference_v<T>
std::is_rvalue_reference_v<T>
T -> int
std::is_lvalue_reference_v<T&>
std::is_rvalue_reference_v<T&>
T& -> int & -> int&
std::is_lvalue_reference_v<T&&>
std::is_rvalue_reference_v<T&&>
T&& -> int && -> int&&
lvalue와 rvalue를 구분하지 못하는 경우 보편 참조를 통해 타입을 분명하게 해주는 것을 완벽 전달이라고 한다.
#include <iostream>
#include <type_traits>
using namespace std;
void goo(int& value) {
cout << "int&" << endl;
}
void goo(int&& value) {
cout << "int&&" << endl;
}
template<typename T>
void foo(T&& value) {
goo(static_cast<T&&>(value));
goo(std::forward<T>(value));
}
int main() {
foo(10);
cout << endl;
int num = 10;
foo(num);
}
실행 결과
int&&
int&&
int&
int&
static_cast<T&&>(value)
std::forward<T>(value)
lvalue T = int& && -> int& value
(T = int) && -> int&& value
foo(T&& val) 같은 경우는 전달 참조