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?

7
  • Why are you trying to take address of r-value reference?
    – xinaiz
    Commented Feb 19, 2017 at 15:46
  • @BlackMoses To check the r-valueness.
    – LogicStuff
    Commented Feb 19, 2017 at 15:47
  • @LogicStuff Ah, okay, thought it was a misconception of & usage :)
    – xinaiz
    Commented Feb 19, 2017 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
    Commented Feb 19, 2017 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. Commented Feb 19, 2017 at 15:53

2 Answers 2

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.

6
  • Thanks a lot! I checked it with this code 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
    Commented Feb 19, 2017 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
    Commented Feb 19, 2017 at 16:14
  • Rvalue references with names are lvalues. Until you understand that you will remain confused. Commented Feb 19, 2017 at 16:24
  • I think I understand that. The thing I don't understand is why standart implementation: typename remove_reference<T>::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
    Commented Feb 19, 2017 at 17:01
  • @fminkin Take the types foo, foo&, foo&&, foo const& and feed them to typename remove_reference<T>::type& and T&& respectively. Pay attention to the rules of reference collapsing. 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? Commented Feb 19, 2017 at 17:27
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
1
  • I think you meant. "You can steal resources from an rvalue, but NOT from an lvalue."
    – Lenz
    Commented Feb 19, 2017 at 19:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.