Modern C++ - 전달(보편) 참조/ 완벽 전달

진경천·2024년 3월 19일
0

C++

목록 보기
86/90

보편 참조

함수 탬플릿의 매개 타입이 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&&)

std::move의 정의

_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를 인자로 받을 수 없다.

레퍼런스 겹침 (reference collapsing)

타입을 추론할 때 아래와 같은 레퍼런스 규칙을 따른다.

&& -> &&
&& & -> &
& && -> &
&& && -> &&

예제

#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;

}

foo(num0), foo(num1)

실행 결과

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&

foo(10)

실행결과

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) 같은 경우는 전달 참조

profile
어중이떠중이

0개의 댓글