2

I'm struggling to understand why std::forward has an overload taking an rvalue reference, as documented here. According to this the overload taking an lvalue reference is sufficient to handle all the cases, given the reference collapsing rules. This makes sense to me (at least at my current level of understanding of the reference collapsing), so I decided to put together a small reproducer mimicking the usual factory pattern to investigate the calls to forward:

#include <iostream>

template<typename T> T&& forw(typename std::remove_reference<T>::type &t){
    std::cout<<"forw&" << std::endl;
    return static_cast<T&&>(t);
}

template<typename T> T&& forw(typename std::remove_reference<T>::type &&t){
    std::cout<<"forw&&" << std::endl;
    static_assert(!std::is_lvalue_reference<T>::value, "invalid rvalue to lvalue conversion");
    return static_cast<T&&>(t);
}


template<typename T> void constructor(T &t){
    std::cout << "constructor(T &t)" << std::endl;
}

template<typename T> void constructor(T &&t){
    std::cout << "constructor(T &&t)" << std::endl;
}

template<typename T> void factory(T &&t){
    constructor(forw<T>(t));
}


int main(){
    
  std::string s;
  std::cout << "** Calling factory(s)" << std::endl;
  factory(s);
  std::cout << "\n** Calling factory(std::string())" << std::endl;
  factory(std::string());
  return 0;
}

Compiling with g++ 10.2.0 and running I get:

$ ./a.out 
** Calling factory(s)
forw&
constructor(T &t)

** Calling factory(std::string())
forw&
constructor(T &&t)

So it seems that the overload of forw taking an lvalue reference is always called, and that the argument is always perfectly forwarded to the constructor. So what's the purpose of the second overload?

Nicola Mori
  • 777
  • 1
  • 5
  • 19
  • The cppreference page describes that second overload. And gives an example. Have you tried that example when the second overload is missing? – StoryTeller - Unslander Monica Dec 07 '20 at 09:23
  • IIRC the second overload is useful only when forwarding expressions, as in `template T&& f(T&& t) { return std::forward(t+t); }`. I've never used such thing though. – YSC Dec 07 '20 at 09:28
  • 1
    Does this answer your question? [What is the purpose of std::forward()'s rvalue reference overload?](https://stackoverflow.com/questions/56361755/what-is-the-purpose-of-stdforwards-rvalue-reference-overload) Also see the [design document](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2009/n2951.html) for cases where the committee thought that a `std::forward` that accepts rvalues would be useful. – HTNW Dec 07 '20 at 09:29
  • @StoryTeller-UnslanderMonica sorry I overlooked that. It's clear now, thanks. As YSC says it must be a very rare case, almost never found in forwarding cases. But I understand that the standard must account for it. – Nicola Mori Dec 07 '20 at 09:34

0 Answers0