C++11 perfect forwarding and reference collapsing

1.2k views Asked by At

Consider this code:

template<typename T>
void foo(T&& param){ //In this case && is called universal reference
    std:string tmp = std::forward<string>(param);
}

My question is if universal reference type can be deduced why do I still need to call forward ?
Why without forwarding tmp's correct c'tor won't be called even if T's type was deduced.

My second question is about reference collapsing rules:

  1. A& && becomes A&
  2. A&& && becomes A&&

so according this rules and taking in account universal reference why std::forward signature can't be as follows:

template<class T> 
T&& forward(T&& arg){
    return static_cast<T&&>(arg);
}

According to the rules from above if T's type is rvalue reference it will collapse to rvalue reference , if T's type is lvalue reference it will collapse to lvalue reference.
So why std::forward have two different signatures one for lvalue reference and one for rvalue reference am I missing something ?

1

There are 1 answers

15
Jonathan Wakely On BEST ANSWER

My question is if universal reference type can be deduced why do I still need to call forward ?

Because as soon as you give a name to the parameter param it is an lvalue, even if the function was called with an rvalue, so it wouldn't be forwarded as an rvalue unless you use forward<T>

Why without forwarding tmp's correct c'tor won't be called even if T's type was deduced.

Because param is an lvalue. To restore the value category of the argument passed to foo you need to cast it back to string& or string&& which means you need to know the type T was deduced as, and use forward to do the cast.

So why std::forward have two different signatures one for lvalue reference and one for rvalue reference am I missing something ?

It was changed by http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3143.html

There is lots of background info in http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3143.html and http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2009/n2951.html

The problem with your suggested version is that if you say forward<string> then the parameter T is not deduced so doesn't function as a forwarding reference, which means that T&& can't bind to an lvalue, and it needs to be able to bind to an lvalue in order for forward<string>(param) to work, because param is an lvalue there.