std::forward
std::move는 인자를 무조건 R-Value로 캐스팅하여 이동 생성/연산자를 사용할 수 있도록 하는 함수
std::forward는 보편 참조 (Universal Reference) 매개변수를 받아서 호출자가 넘긴 value category(l-value, r-value)를 보존하여 다시 넘겨주는 함수
- L-Value로 들어왔으면 L-Value로, R-Value로 들어왔으면 R-Value로 캐스팅 됨
기본 형태
template <typename T, typename... Args>
void wrapper(T&& t, Args&&... args) {
callee(std::forward<T>(t),
std::forward<Args>(args)...);
}
- L-Value 인자가 들어오면
T는 T&로 추론되며 std::forward<T>(t)는 L-Value로 캐스팅 됨
- R-Value 인자가 들어오면
T는 그대로 T로 추론되며 std::forward<T>(t)는 R-Value로 캐스팅 됨
대표적인 예시
- 래퍼/팩토리/헬퍼 함수에서 인자를 있는 그대로 다른 함수/생성자에게 넘길 때
template <typename T, typename... Args>
std::unique_ptr<T> make_ptr(Args&&... args) {
return std::unique_ptr<T>(
new T(std::forward<Args>(args)...)
);
}
- 컨테이너의
emplace
template <typename... Args>
void emplace_back(Args&&... args) {
new (&storage[size]) T(std::forward<Args>(args)...);
++size;
}
// forward를 통해 l-value 인자는 복사, r-value 인자는 이동이 됨
핸들러의 완벽한 전달
// ContextManager.h
class ContextManager
{
// ...
public:
template <typename CompletionHandler>
auto RegisterWork(CompletionHandler handler)
{
asio::post(_io, std::forward<CompletionHandler>(handler));
}
// ...
};
- Asio 라이브러리를 사용하면서
io_context에 대한 래퍼 클래스를 통해 post함수를 RegisterWork로 제공하면서 기존의 asio.post를 사용하는 것과 동일하게 핸들러를 전달하도록 하기위해 std::forward를 사용하여 호출 당시 핸들러 함수를 그대로 asio.post에 전달하도록 함
주의점
- 보편 참조(Universal Reference)가 아닌 평범한 인자에는
std::forward를 사용하지 않음
std::forward<T>(val)에서 T는 반드시 함수 템플릿 형식의 인자여야 하며, 호출 시 그대로 전달해야 함
Comments