Why std::forward return a rvalue when transferring a lvalue?

81 Views Asked by At

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?

1

There are 1 best solutions below

0
eerorika On

If std::forward<A>(p1) forwards p1 as rvalue, then what's the difference of std::forward and std::move?

The difference is that std::forward<A&>(p1) returns an lvalue, and std::move does not.

You're misusing std::forward. Passing a constant template type argument is pointless. They're useful when a function template has a forwarding reference argument whose type is deduced from function argument: Example:

template<class T>
void foo(T&& ref) {
    T t = std::forward<T>(ref); // copy or move

...
foo(p1); // T is deduced as A&