9

It has come to my attention that std::forward is useless in this context:

void consumeObject(std::unique_ptr<Object>&& robj) {
  myvector.emplace_back(std::forward<std::unique_ptr<Object>>(robj));
}

Why is that? I thought that by the time an rvalue reference binds to a function parameter (i.e. I can reference it by name) it becomes an lvalue in that scope and in order to be passed forward needs to be casted to an rvalue reference (as in perfect forwarding).

Why is it wrong/useless?

Dean
  • 6,610
  • 6
  • 40
  • 90
  • 8
    "Casting to rvalue reference" is not "perfect forwarding". It's *moving*. – Kerrek SB Nov 30 '15 at 13:18
  • Perfect forwarding is the combination of a special template argument deduction rule and reference collapsing. – Simple Nov 30 '15 at 13:21
  • 2
    Also: perfect forwarding involves "universal references", and you don't have a universal reference; you have an rvalue reference. `std::move` is sufficient here. – Simple Nov 30 '15 at 13:23
  • Is the difference between std::move and std::forward purely **semantic**? I.e. they are implemented in the same way and they do exactly the same thing but are meant to be used in two different contexts (hence the two names)? – Dean Nov 30 '15 at 13:27
  • I agree. In this case, `std::move` would have made *better* sense, though `std::forward` produces the same *move* effect, just that it is *misleading* to the reader, in that sense it is *wrong*. – Nawaz Nov 30 '15 at 13:31
  • See http://stackoverflow.com/q/9671749 and maybe http://stackoverflow.com/q/13219484. – Kerrek SB Nov 30 '15 at 13:34
  • 2
    @Dean No, the implementation differs. `std::move` is intended to be used without an explicitly specified template argument, and it's not immediately obvious what happens if you do specify it. `std::forward` is deliberately designed so that it *requires* an explicit template argument. – Angew is no longer proud of SO Nov 30 '15 at 13:43
  • 4
    @Simple, it's called a [forwarding reference](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4164.pdf), not a universal reference, please use the new and approved name. – Jonathan Wakely Nov 30 '15 at 13:51
  • @JonathanWakely ok. I don't like the name universal reference anyway, that's why I quoted it. – Simple Nov 30 '15 at 13:52
  • 1
    @Simple, lots of people didn't like it, that's why a better name was found, and added to the C++ working draft :) (see the link I added to my previous comment for the history) – Jonathan Wakely Nov 30 '15 at 13:55
  • 3
    @Dean, `std::move` always casts to an rvalue reference, but `std::forward` sometimes casts to an lvalue reference and sometimes casts to an rvalue reference, depending on the template argument `T`. That difference is very important! The whole point of `std::move` is to convert to an rvalue. The whole point of `std::forward` is to convert back to the original value category of the object before you bound a forwarding reference to it. So they do not do the same thing. – Jonathan Wakely Nov 30 '15 at 13:59
  • What is `myvector` in this example? – M.M Dec 01 '15 at 07:57

1 Answers1

14

It's not wrong to use std::forward here (per se), but it is inappropriate and thus misleading (in that sense, you could say that it is actually wrong).

The way you spell "cast something to an rvalue reference to its type" is std::move. That is the only thing that std::move does—it's a static_cast<T&&> where you don't have to spell out the T.

std::forward is a different beast, intended for use with perfect forwarding. Perfect forwarding only occurs in templates, where forwarding references are possible. A forwarding reference is a reference which, thanks to a special rule in the language, can deduce to either an lvalue reference or an rvalue reference. There, you need std::forward<T> to re-instate the appropriate value category (lvalue or rvalue).

Toy example:

void print(int &)
{
  std::cout << "Lvalue\n";
}

void print(int &&)
{
  std::cout << "Rvalue\n";
}

template <class T>
void perfectForwarder(T &&v)
{
  print(std::forward<T>(v));
}

int main()
{
  int i;
  perfectForwarder(i);
  perfectForwarder(std::move(i));
  perfectForwarder(42);
}

The output will be:

Lvalue
Rvalue
Rvalue

[Live]

Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455
  • 3
    I would say that `std::forward` definitely is wrong here, in the same way that unreadable code is wrong regardless of whether it works or not. – bames53 Nov 30 '15 at 13:32
  • Thanks! Now I got it! – Dean Nov 30 '15 at 13:34
  • 3
    I'd not say it is *"unnecessary"*. It is rather *misleading* (to the reader). – Nawaz Nov 30 '15 at 13:36
  • It would be nice to elaborate a bit more on what perfect forwarding is here and that this restricts forwarding rvalue as an lvalue, which will result in compile time error. – g24l Nov 30 '15 at 13:41
  • @Angew: Saw the update, but again, it is "misleading" not *because* it is "unnecessary", rather because it is *inappropriate*. The appropriate tool is `std::move` here. – Nawaz Nov 30 '15 at 13:45
  • 1
    @g24l Perfect forwarding, by itself, has little to do with compile-time errors. Indeed, in my toy example, omitting `std::forward` would *not* result in an error. All `std::forward` does is, as the answer states, "re-instate the appropriate value category" – Angew is no longer proud of SO Nov 30 '15 at 13:45
  • @Angew check the comments in the question, they elaborate more than I will ever be able to in this comment. – g24l Nov 30 '15 at 13:47
  • @Angew: I edited the answer. Hope you dont mind that. – Nawaz Nov 30 '15 at 13:51
  • By the way, don't get me wrong it is not needed here, because the function is not templates, so the use case for forward will not happen in the example code. – g24l Nov 30 '15 at 13:51
  • @Nawaz No. I just re-worded the edit slightly; hope you don't mind *that* ;-) – Angew is no longer proud of SO Nov 30 '15 at 14:25