모두의 코드
C++ 레퍼런스 - std::forward 함수
std::forward
<utility> 에 정의됨
// 참고로 C++ 14 이전 버전에는 constexpr 이 아니다. template <class T> constexpr T&& forward(std::remove_reference_t<T>& t) noexcept; // (1) template <class T> constexpr T&& forward(std::remove_reference_t<T>&& t) noexcept; // (2)
1 번 오버로딩의 경우 lvalue 를 T
에 따라 lvalue 혹은 rvalue 로 전달한다.
만일 t
가 보편적 레퍼런스(Universal reference) 라면, (1) 번 오버로딩 함수의 경우 t
를 다른 함수에 값 카테고리를 그대로 유지한채 전달하게 된다. 참고로 보편적 레퍼런스는, 템플릿 인자 T 를 인자로 받는 우측값 레퍼런스로 예를 들어서;
template <class T> void wrapper(T&& arg) { // arg is always lvalue foo(std::forward<T>(arg)); // Forward as lvalue or as rvalue, depending on T }
위 경우 arg
가 보편적 레퍼런스 이다. (템플릿 인자 T
의 우측값 레퍼런스 이므로). 이 때 arg
자체는 lvalue 이므로 1 번 std::forward 가 오버로딩 된다.
만일
wrapper()
에 rvalue std::string 을 전달한다면,T
는 std::string 으로 추론된다 (std::string&, const std::string&, std::string&&
이 아님!) 그리고 std::forward 가foo
에 우측값 레퍼런스를 전달하게 된다.만일
wrapper()
에 상수 lvalue std::string 을 전달한다면,T
는const std::string&
으로 추론되며, std::forward 가foo
에 상수 좌측값 레퍼런스를 전달한다.만일
wrapper()
에 상수가 아닌 lvalue std::string 을 전달한다면T
는std::string&
으로 추론되고, std::forward 는 상수가 아닌 좌측값 레퍼런스를foo
에 전달한다.
2 번 오버로딩의 경우 rvalue 는 rvalue 로 전달하고, rvalue 가 lvalue 로 전달되는 것을 막는다.
사실 두 번째 오버로딩의 경우 필요한 이유가 조금 복잡하다. 예를 들어서 아래와 같은 상황을 생각해보자.
std::forward<T>(u.get());
만일 사용자가 u.get()
이 lvalue 를 리턴할지, rvalue 를 리턴할지 모른다고 해보자. 하지만 T
가 좌측값 레퍼런스 타입이 아니라면, u.get()
이 리턴한 것을 이동 시키고 싶을 것이다. 반면에 T
가 좌측값 레퍼런스 타입이라면 u.get()
이 리턴한 것을 이동하면 안된다
사실 두 번째 형태의 forward 오버로딩을 사용할 경우는 거의 없을 테므로 굳이 걱정할 필요는 없다. 두 번째 형태의 forward 가 왜 필요한지 자세한 설명은 표준 문서를 읽어보는 것을 권장한다.
인자들
t : 전달할 객체
리턴값
static_cast<T&&>(t)
실헹 예제
#include <iostream> #include <memory> #include <utility> struct A { A(int&& n) { std::cout << "rvalue overload, n=" << n << "\n"; } A(int& n) { std::cout << "lvalue overload, n=" << n << "\n"; } }; class B { public: template <class T1, class T2, class T3> B(T1&& t1, T2&& t2, T3&& t3) : a1_{std::forward<T1>(t1)}, a2_{std::forward<T2>(t2)}, a3_{std::forward<T3>(t3)} {} private: A a1_, a2_, a3_; }; template <class T, class U> std::unique_ptr<T> make_unique1(U&& u) { return std::unique_ptr<T>(new T(std::forward<U>(u))); } template <class T, class... U> std::unique_ptr<T> make_unique2(U&&... u) { return std::unique_ptr<T>(new T(std::forward<U>(u)...)); } int main() { auto p1 = make_unique1<A>(2); // rvalue int i = 1; auto p2 = make_unique1<A>(i); // lvalue std::cout << "B\n"; auto t = make_unique2<B>(2, i, 3); }
실행 결과
rvalue overload, n=2 lvalue overload, n=1 B rvalue overload, n=2 lvalue overload, n=1 rvalue overload, n=3
참고 자료
move : 객체의 우측값 레퍼런스를 얻는다.
move_if_noexcept
: 생성자가 예외를 던지지 않을 경우에만 우측값 레퍼런스를 얻는다.
댓글을 불러오는 중입니다..