1

According to Is a member of an rvalue structure an rvalue or lvalue?:

if E1 is lvalue, then E1.E2 is lvalue, and forward cast its argument to an rvalue only if that argument is bound to an rvalue. In function void foo(Obj &&obj) below, obj is lvalue, so obj.i is lvalue, why is std::forward<int>(obj.i) an rvalue?

class Obj
{
    public:
        int i;
};


void foo(int &i)
{
    cout<<"foo(int&)"<<endl;
}

void foo(int &&i)
{
    cout<<"foo(int&&)"<<endl;
}

void foo(Obj &&obj)
{
    foo(std::forward<int>(obj.i));
    foo(obj.i);
}

int main()
{
    Obj obj;
    foo(std::move(obj));
    return 0;
}

output

foo(int&&)
foo(int&)
Community
  • 1
  • 1
camino
  • 10,085
  • 20
  • 64
  • 115

3 Answers3

0

function void foo(Obj &&obj) below:

obj is lvalue, so obj.i is lvalue,

Hmm, do you declare obj as rvalue reference in function proto and then insist it's an lvalue?

bipll
  • 11,747
  • 1
  • 18
  • 32
0

According to the specifications of std::forward, the return type is simple an rvalue reference applied to the template type. So the return type std::forward<int> is int&& - used in this way it has exactly the same effect as std::move.

The normal recipe for using std::forward is with universal references:

template<typename T>
void f(T&& val)
{
     other_func(std::forward<T>(val));
}

This would work correctly for references, as the deduced type for an lvalue reference in this case would also be an lvalue reference. In your case you're hard-coding the type (to int) rather than deducing it - the deduced type if you used the above pattern would in fact be int&, not int.

You will see that if you change foo(std::forward<int>(obj.i)) to foo(std::forward<int&>(obj.i)) you will get what you expect

Smeeheey
  • 9,906
  • 23
  • 39
0

You're actually forwarding the wrong thing. In this function:

void foo(Obj &&obj)
{
    foo(std::forward<int>(obj.i));
    foo(obj.i);
}

obj has name, so it's an lvalue. obj.i is an lvalue in both cases, just in the first you're explicitly casting it to an rvalue! When forward gets a reference type, you get out an lvalue. When it gets a non-reference type, you get an rvalue. You're giving it a non-reference type, so the forward here is equivalent to: foo(std::move(obj.i)); Does that make it clearer why you get an rvalue?

The question you linked, however, is about members of rvalues. To get that, you need to turn obj itself into an rvalue:

foo(std::move(obj).i);

Here, since std::move(obj) is an rvalue, std::move(obj).i is an rvalue as well.


Regardless, using forward when taking an argument by not-forwarding-reference is a little weird. Here's a more general example:

template <class O>
void foo(O&& obj) {
    foo(std::forward<O>(obj).i);
}

foo(obj);            // calls foo(int&)
foo(std::move(obj)); // calls foo(int&&)

In the former case, std::forward<O>(obj) is an lvalue because O is Obj&, which makes std::foward<O>(obj).i an lvalue. In the latter case, they're both rvalues.

Barry
  • 286,269
  • 29
  • 621
  • 977