0

Recently I've been trying to understand move semantics and came up with a question.

The question has already been discussed here.

I implemented the first variant and checked whether it returns l-value or r-value:

#include <iostream>
using namespace std;

template <typename T>
T&& my_forward(T&& x) {
    return static_cast<T&&> (x);
}

int main() {
    int a = 5;
    &my_forward(a); // l-value
    return 0;
}

So if I pass l-value, it returns l-value (compiles, because I can take an adress from l-value) and if I do that:

&my_forward(int(5)); // r-value with int&& type

My code doesn't compile, because my_forward returned r-value. In the question above they say that the difference between this implementation and standart one (with std::remove_reference and 2 different arguments with & and && respectively) is that my implementation returns l-value all the time, but as I've shown it returns both r-value and l-value.

So I wonder, why can't I implement std::forward like that? In what specific cases will it show difference between standart one? Also, why should I specify T as a template and can't let it define itself with argument type?

Community
  • 1
  • 1
fminkin
  • 162
  • 2
  • 10
  • Why are you trying to take address of r-value reference? – xinaiz Feb 19 '17 at 15:46
  • @BlackMoses To check the r-valueness. – LogicStuff Feb 19 '17 at 15:47
  • @LogicStuff Ah, okay, thought it was a misconception of `&` usage :) – xinaiz Feb 19 '17 at 15:48
  • to check whether it's r-value or l-value. Basically to prove wrong first answer in the linked post **The problem with the first is that you can write std::forward(x), which doesn't do what you want, since it always produces lvalue references.** – fminkin Feb 19 '17 at 15:50
  • You really should be testing this with forwarding references - which you can only have with template parameters. Testing in main with variables, you can not pass in forwarding references to your my_forward. – Richard Critten Feb 19 '17 at 15:53
  • What about the answer to the linked question did you find unsatisfactory? – Barry Feb 19 '17 at 16:00
  • @Barry well I thought, hey, it works fine in arificial context, but they said it return only l-values, so I found it confusing – fminkin Feb 19 '17 at 16:15

2 Answers2

3

Try hsing it like std forward in a real context. Yours does not work;

void test(std::vector<int>&&){}

template<class T>
void foo(T&&t){
  test(my_forward<T>(t));
}

foo( std::vector<int>{} );

The above does not compile. It does with std::forward.

Your forward does nothing useful other than block reference lifetime extension. Meanwhile, std::forward is a conditional std::move.

Everything with a name is an lvalue, but forward moves rvalue references with names.

Rvalue references with names are lvalues.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • Thanks a lot! I checked it with this [code](http://ideone.com/XPePam) and it seems I was wrong. I didn't check it in a real context, because I thought (teoretically) that those implementations were the same. I can't really understand what's the difference. Can you explain it? – fminkin Feb 19 '17 at 16:13
  • And why exactly my implementation works like this? As we saw in the topic, it returns r-value, why doesn't it work then? – fminkin Feb 19 '17 at 16:14
  • **Rvalue references with names are lvalues.** Until you understand that you will remain confused. – Yakk - Adam Nevraumont Feb 19 '17 at 16:24
  • I think I understand that. The thing I don't understand is why standart implementation: typename remove_reference::type& t works differently than T&& t. In function body it'll be lvalue anyway (because it has a name) the only difference is in the argument – fminkin Feb 19 '17 at 17:01
  • @fminkin Take the types `foo`, `foo&`, `foo&&`, `foo const&` and feed them to `typename remove_reference::type&` and `T&&` respectively. Pay attention to the rules of [reference collapsing](https://stackoverflow.com/questions/13725747/concise-explanation-of-reference-collapsing-rules-requested-1-a-a-2). They work differently because they are very different expressions. The standard one, for example, can *never* be an rvalue reference. How could they be the same? – Yakk - Adam Nevraumont Feb 19 '17 at 17:27
  • It seems, I miscomprehended reference collapsing rule, I'll fiddle with that types and figure everything out than. Thanks a lot for the explanation. – fminkin Feb 19 '17 at 17:31
1

Unfortunately, taking the address is not a useful operation in your context, because it looks at the wrong kind of value category:

  • You can take the address of a glvalue, but not of a prvalue. A glvalue represents a "location" (i.e. where an object is), a prvalue represents "initialization" (i.e. what value an object has).

  • You can steal resources from an rvalue, but not from an lvalue. Lvalue references bind to lvalues, rvalue references bind to rvalues. The point of std::forward is to cast an argument to an rvalue when an rvalue was provided, and to an lvalue when an lvalue was provided.

When std::forward returns an rvalue, it actually returns an xvalue, and xvalues are both rvalues and glvalues:

                    lvalue       f() for "T& f();",   decltype(f()) is T&
                  /
          glvalue
        /         \
  value             xvalue       f() for "T&& f();",  decltype(f()) is T&&
        \         /
           rvalue
                  \
                    prvalue      f() for "T f();",    decltype(f()) is T
Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • I think you meant. "You can steal resources from an rvalue, but NOT from an lvalue." – Lenz Feb 19 '17 at 19:48