On this page
article
C++ Perfect Forwarding
Perfect Forwarding
정의
전달받은 인자를 값, const/volatile(cv) 속성, value category(lvalue/rvalue) 등의 변화 없이 그대로 다른 함수에 전달하는 기법.
필요성
- 특정 함수(예:
hoo
)가 rvalue 참조를 인자로 받지만, forwarding 함수(chronometry
)에서 이를 받는 순간 lvalue가 되어 호출이 안 됨. - 이를 해결하기 위해 캐스팅 또는 forwarding이 필요.
초기 예제
void foo(int n) {}
void goo(int& r) { r = 20; }
void hoo(int&& r) {}
template<class F, class T>
void chronometry(F f, int& arg) { f(arg); } // lvalue 버전
// hoo를 위해서는 rvalue 버전도 필요
// 1. int&& arg로 받되 static_cast 필요
template<class F, class T>
void chronometry(F f, int&& arg) {
f(static_cast<int&&>(arg));
}
논의
어차피 int&, int&& 지원을 위해 여러 버전이 필요한 것이라면 템플릿으로 자동 생성하자 근데 그러려면 함수의 구현부가 동일해야함
대안
int&버전도 캐스팅을 진행하면 동일.
어차피 컴파일 단계에서 동일한 타입으로 캐스팅이라 생략됨
std::forward
개선: std::forward 사용
template<class F, class T>
void chronometry(F f, T&& arg) {
f(std::forward<T>(arg));
}
- T&&: forwarding reference (lvalue/rvalue 모두 받음)
- std::forward
: 전달받은 value category 그대로 전달
가변인자 지원 및 반환값 전달
template<class F, class... Args>
decltype(auto) chronometry(F&& f, Args&&... args) {
return std::invoke(std::forward<F>(f), std::forward<Args>(args)...);
}
- decltype(auto): 반환값의 cv/ref 속성 보존
- std::invoke: 함수 포인터, 멤버 함수 포인터, 함수 객체 모두 호출 가능
예제 사용
Functor f;
chronometry(f, 10);
chronometry(Functor(), 10);
유의사항
- 포인터 인자일 경우
nullptr
사용. (예:chronometry(f, nullptr)
) - 오버로딩 함수는 명확히 캐스팅 후 전달.
chronometry(static_cast<void(*)(int, int)>(func), 1, 2);