Why are there two signatures of std :: forward?

As stated in cplusplus.com , std::forward has two signatures:

 template <class T> T&& forward (typename remove_reference<T>::type& arg) noexcept; template <class T> T&& forward (typename remove_reference<T>::type&& arg) noexcept; 

A typical use of std::forward is to preserve rvalueness when passing arguments to other functions. We illustrate this with an example:

 void overloaded(int &) { std::cout << "lvalue"; } void overloaded(int &&) { std::cout << "rvalue"; } template <typename T> void fwd(T && t) { overloaded(std::forward<T>(t)); } 

When we call fwd(0) , T prints int ( T is of type int && ). Then we will call std::forward<int>(t) . The result of this call is an expression of type int && , so the second version of the overloaded function is selected, and the program outputs "rvalue" to standard output.

When we call fwd(i) (where I am some int variable), T prints int& ( T is of type int & ). Then we will call std::forward<int&>(t) . The result of this call (after applying the rules for dropping links) is an expression of type int & , therefore, the first version of the overloaded function is selected, and the program outputs "lvalue" to standard output.

In both cases, we use the first overload std::forward (accepting typename remove_reference<T>::type& arg ). This is because even if the type T is int && , it binds to the lvalue reference (because the named variable of type "rvalue reference to something" itself is an lvalue and lvalues ​​cannot bind to the rvalue reference).

Question 1:

What is the second overload of std::forward for? Can you come up with some practical example that uses an overload that takes arg by rvalue reference?

Question 2:

cplusplus.com says:

Both signatures return the same as:

 static_cast<decltype(arg)&&>(arg) 

The problem I am facing is that I am sure that this is not correct. When we try to return this from the first std::forward overload, we get a compilation error.

When fwd is called with int rvalue, it calls the first overload of std::forward with T = int . Then decltype(arg) will become int& , so static_cast<decltype(arg)&&>(arg) will crash to static_cast<int&>(arg) . But the return type is int && , and we get a compilation error:

 cannot bind 'std::remove_reference<int>::type {aka int}' lvalue to 'int&&' 

Both overloaded versions of std::forward should return static_cast<T&&>(arg) . I'm right?

Do you think that a quote from cplusplus.com is a mistake?

+5
source share
1 answer
 class Foo {}; auto f = [](){ return Foo{}; }; auto g = []()->Foo&{ static Foo x; return x; }; template<class T> std::false_type is_rvalue( T& ) { return {}; } template<class T> std::true_type is_rvalue( T&& ) { return {}; } template<class T, class F> auto test( F&& f ) { return is_rvalue( std::forward<T>( f() ) ); } int main() { std::cout << test<Foo>(f) << "," << test<Foo&>(g) << "\n"; } 

This is a little far-fetched; but imagine where you have a factory that can create Foo or Foo& depending on some irrelevant details, but you know that you want to forward it based on the second type of T to T&& . You know that if T not a link, then it will not create Foo , but if T is a link, it can.

+1
source

Source: https://habr.com/ru/post/1259911/


All Articles