1

I do understand the nature of std::forward when it comes to, well, forwarding a lvalue argument as a lvalue or rvalue, depending on the template parameter's "value-ness".

But I just noticed that this page of cppreference.com contains the other way of using it, namely with rvalues. Declaration is as follows:

template< class T >
T&& forward( typename std::remove_reference<T>::type&& t ) noexcept;
//note double ampersand

And the brief description is like this:

  1. Forwards rvalues as rvalues and prohibits forwarding of rvalues as lvalues

What problem does it solve? The only reason I was able to come up with is that it may be used as an additional check for correctness of wrapper function call, that is, if I plan to supply the internal function with an rvalue, then the wrapper should be called solely with an rvalue.

Anton Tretyakov
  • 303
  • 1
  • 9
  • 2
    I'm pretty sure [Howard answers this](https://stackoverflow.com/q/29135698/817643), along with receipts. – StoryTeller - Unslander Monica Jul 20 '22 at 19:42
  • 1
    Have a look at the example on cppreference. The use case is to write correct, optimized template code. – Simon Kraemer Jul 20 '22 at 19:45
  • 1
    The first overload cannot turn a rvalue into an rvalue reference and this is expected of `std::forward`. – fabian Jul 20 '22 at 19:50
  • 1
    @SimonKraemer I'm looking at the cppreference example, and it boils down to `std::forward(foo())` which looks like a useless identity transformation. Note that OP asks only about the overload with the `&&` argument. – HolyBlackCat Jul 20 '22 at 19:59
  • @HolyBlackCat thank you for introducing the `identity transformation` notion. My question was mostly about it - why paying any additional attention to prvalue, if it is already clear that it is a prvalue? – Anton Tretyakov Jul 20 '22 at 21:32
  • @StoryTeller-UnslanderMonica hey! I've read the referred answer and the B use case in the mentioned article, and this is not yet clear for me why exactly it is needed. The example that they are providing seems to be rather contrived to me. `C(U&& u) : t_(std::forward(std::move(u).get())) {}` - in this example from the article, why would I even bother putting an output of std::move into std::forward? It is already a prvalue, why forwarding? – Anton Tretyakov Jul 20 '22 at 21:39
  • @fabian but I am just failing to see why would I want that (probably because I'm not an experienced cpp dev, I do recognize that). It is done for the sake of covering all the use cases possible *just in case*? – Anton Tretyakov Jul 20 '22 at 21:42
  • 1
    @AntonTretyakov - It's not the output of `move`, it's the output of `get()` you are attempting to forward. Imagine `T` is some `R&`, and that `U::get() &&` returns a `R&&`. If the second overload did not stop here (i.e. it only casted blindly), you'd be holding on to a dangling reference. – StoryTeller - Unslander Monica Jul 20 '22 at 22:28
  • @StoryTeller-UnslanderMonica so, I see `move` is here to get a correct overload of `U::get()`, namely the `&&` one. But that's not exactly clear for me how dangling reference comes into play, could you elaborate on it a little bit more? In addition, in the mentioned article `get()` just return by value - why forwarding? To avoid additional copy? – Anton Tretyakov Jul 21 '22 at 12:06
  • 1
    Returning `R` or `R&&` has the same problem here, so that's not a major difference. Here's the problem illustrated https://wandbox.org/permlink/Tb4H8Je13EjLcwts. – StoryTeller - Unslander Monica Jul 21 '22 at 12:12
  • @StoryTeller-UnslanderMonica the link's giving an error, unfortunately. – Anton Tretyakov Jul 21 '22 at 12:32
  • 1
    https://godbolt.org/z/9EorPWohT – StoryTeller - Unslander Monica Jul 21 '22 at 12:33
  • @StoryTeller-UnslanderMonica yes, so it is blocking rvalue from converting to lvalue, this is the mechanism. But why do I need it? And how it is related to dangling references? I mean, yeah, if I play a bit with your example and make the first template parameter `const` then it will work without `forward` and then I will have a dangling reference in `c` after the temporary `A` is destructed - and in such a case `forward` acts like a safety measure. Is it the case? – Anton Tretyakov Jul 21 '22 at 14:46
  • 1
    If it wasn't blocked, that reference in C would have been bound to the member of the temporary object I initialized with `{}`. That *is* dangling. – StoryTeller - Unslander Monica Jul 21 '22 at 15:31
  • @StoryTeller-UnslanderMonica yes, so that is the safety measure I am talking about. But why would I want to use `get()` at first place, especially combined with `move`? I mean, it seems for me that this second `forward` overload is solving the problem that is purely made-up at first place - my understanding is that you shouldn't bind members of one object to members of another (until they are explicitly shared) at all to start with. – Anton Tretyakov Jul 21 '22 at 19:23
  • 1
    It's not purely made up. That example accepted an rvalue in the constructor (it wasn't a forwarding reference). Objects may overload members to behave differently for rvalues (usually more efficiently). It's only natural to move there to allow such an overload to be called if it exists. The second forward is to adjust the return value to the expected type of the member. Nothing is done "just because". It's a rare usage pattern, but not a contrived one. Either way, comments are not for extended discussions, so I'm signing off here. – StoryTeller - Unslander Monica Jul 21 '22 at 19:47
  • @StoryTeller-UnslanderMonica well, your last comment made something click in my head! Thanks for your time mate, this case now seems to be clear! – Anton Tretyakov Jul 22 '22 at 15:31

0 Answers0