It is said that std::forward() perfectly forwards a lvalue to lvalue, and rvalue to rvalue. But the code below seems to show a different conclusion?
when I run the code below
#include <iostream> class A { public: int x; A() {x = 0;} explicit A(int x) : x(x) {} A& operator= (A&& other) { std::cout << " && =" << std::endl; x = other.x; } A& operator= (const A& other) { std::cout << " & =" << std::endl; x = other.x; } }; int main() { A p1(1), p2, p3; p2 = p1; p3 = std::forward<A>(p1); return 0; } The result is:
& = && = it seems that std::forward<A>(p1) forwards p1 as rvalue.
The source code of std::forward() is:
/** * @brief Forward an lvalue. * @return The parameter cast to the specified type. * * This function is used to implement "perfect forwarding". */ template<typename _Tp> constexpr _Tp&& forward(typename std::remove_reference<_Tp>::type& __t) noexcept { return static_cast<_Tp&&>(__t); } /** * @brief Forward an rvalue. * @return The parameter cast to the specified type. * * This function is used to implement "perfect forwarding". */ template<typename _Tp> constexpr _Tp&& forward(typename std::remove_reference<_Tp>::type&& __t) noexcept { static_assert(!std::is_lvalue_reference<_Tp>::value, "template argument" " substituting _Tp is an lvalue reference type"); return static_cast<_Tp&&>(__t); } gdb shows that when running p3 = std::forward<A>(p1);, it steps into the first function forward(typename std::remove_reference<_Tp>::type& __t) noexcept and returns a value of type A &&
(gdb) p __t $2 = (std::remove_reference<A>::type &) @0x7fffffffd1cc: { x = 1 } (gdb) p static_cast<_Tp&&>(__t) $3 = (A &&) @0x7fffffffd1cc: { x = 1 } I am totally confused. Would you please explain what happened when calling std::forward()? If std::forward<A>(p1) forwards p1 as rvalue, then what's the difference of std::forward and std::move?
forwardis just a conditional move. If the template argument isT &it does nothing, and otherwise it acts as astd::move. It's intended to be used with forwarding references. When outside of a template, there's no point in using it, since you can unconditionallystd::moveor not move.std::forwardis meant to be used with reference collapsing, which is how universal references work. It doesn't make sense to pass it a non-reference type, it isn't an expected use case for that function. Trystd::forward<A&>, which will give you the desired behaviour, which is also equivalent to not usingstd::forwardat all. It is basically meant to deduced whether or not tostd::moveshould be used based on what kind of reference a template argument is.