* 정의

 

std::forward는 템플릿 함수에서의 T&& 형식을 호출자가 전달하는데 사용된 범주(왼값(lvalue) 또는 오른값(rvalue))로 캐스팅하는 역할을 수행한다. 간단히 설명하면 템플릿 함수에서 보편 참조 매개변수를 받아 다른 곳으로 넘길 때 해당 매개변수를 초기화하는 데 쓰인 인수의 형식으로 캐스팅한다는 뜻이다. 이러한 체계를 완벽 전달(perfect forwarding)이라고 한다.

 

단순히 std::move는 무조건 오른값으로 캐스팅, std::forward는 조건부 캐스팅이라고 생각하면 편하다.

 

 

* 사용 이유

 

템플릿 함수에서 보편 참조 매개변수를 다른 곳에서 사용해야할 때 (해당 매개변수를) 초기화하는 데 쓰인 인수의 형식 그대로 넘겨주고 싶을 때 std::forward를 사용한다.

 

 

* 동작 방식

 

- 기본 동작 방식

 

초기화하는 데 쓰인 인수의 형식으로 캐스팅하기 위해서 정보가 필요하다. 이는 std::forward<T>의 T에서 확인하여 캐스팅할 수 있다.

template<typename T>
T&& forward_14(std::remove_reference_t<T>& param)
{
	return static_cast<T&&>(param);
}

이와 같이 구현되는데, 신기하게 T가 int형이면 오른값(int&&)을 돌려주고 int& 형이면 왼값(int&)을 돌려준다. 이는 보편 참조에서 전달되는 인수가 오른값일 때 T가 int형이기 때문이다. 즉 오른값 참조를 반환하려면 T 형식이 참조형식이 아니여야 한다. (아래 예시의 주석을 확인해보면 알 수 있다.)

 

사실 그냥 T에 int&& 형을 넣어도 참조 축약으로 인해 오른값(int&&)을 돌려준다. 이 사실은 템플릿과 같이 T를 받아올 수 없는 경우(람다 표현식)에서 유용하게 사용된다.

 

 

- 보편 참조 예시

 

간단히 예시를 들어보면 다음과 같다.

void Process(const std::string& lvalue)
{
	std::cout << "lvalue version" << '\n';
}

void Process(std::string&& rvalue)
{
	std::cout << "rvalue version" << '\n';
}


template<typename T>
void CallProcess1(T&& param) noexcept
{
	Process(param);
}

template<typename T>
void CallProcess2(T&& param) noexcept
{
	Process(std::forward<T>(param));
}


void Forward_Test(void)
{
	std::string input;
	
	CallProcess1(input);
	CallProcess1(std::move(input));
	
	std::cout << '\n';
	// T : std::string& / param : std::string&
	CallProcess2(input);

	// T : std::string / param : std::string&&
	CallProcess2(std::move(input));
}
lvalue version
lvalue version

lvalue version
rvalue version

 

 

* 주의 사항

 

1. std::move대신 std::forward를 사용해도 되지만 T 형식에 주의해야 한다.

 

std::move 버전

class Annotation
{
public:
    // 이동 생성을 수행한다.
    Annotation(MyString&& text)
        : _text(std::move(text)) {}
        
    // ...
public:
    MyString _text;
};

 

std::forward 버전

class Annotation
{
public:
    // 이동 생성을 수행한다.
    Annotation(MyString&& text)
        : _text(std::forward<MyString>(text)) {}
        
    // ...
public:
    MyString _text;
};

T 형식이 참조형식이 아니여야 한다. 이는 전달되는 인수가 오른값임을 부호화하는 데 쓰이는 관례이다. 잘못해서 T에 MyString&을 설정하면 이동 생성이 아닌 복사 생성을 수행하게 된다.

 

 

'C++' 카테고리의 다른 글

보편 참조에 대한 중복적재 대안  (0) 2022.03.31
보편 참조와 오른값 참조  (0) 2022.03.27
std::move  (0) 2022.03.26
스마트 포인터 - std::weak_ptr  (0) 2022.03.23
스마트 포인터 - std::shared_ptr  (0) 2022.03.22

+ Recent posts