* 정의
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 |