Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are there any use cases for std::forward with a prvalue?

Tags:

The most common usage of std::forward is to, well, perfect forward a forwarding (universal) reference, like

template<typename T> void f(T&& param) {     g(std::forward<T>(param)); // perfect forward to g } 

Here param is an lvalue, and std::forward ends up casting it to a rvalue or lvalue, depending on what the argument that bounded to it was.

Looking at the definition of std::forward from cppreference.com I see that there is also a rvalue overload

template< class T > T&& forward( typename std::remove_reference<T>::type&& t ); 

Can anyone give me any reason why the rvalue overload? I cannot see any use case. If you want to pass a rvalue to a function, you can just pass it as is, no need to apply std::forward on it.

This is different from std::move, where I see why one wants also a rvalue overload: you may deal with generic code in which you don't know what you're being passed and you want unconditional support for move semantics, see e.g. Why does std::move take a universal reference?.

EDIT To clarify the question, I'm asking why overload (2) from here is necessary, and a use case for it.

like image 867
vsoftco Avatar asked Apr 25 '15 00:04

vsoftco


People also ask

Where to use std:: forward?

It is typically used with template functions where reference collapsing may have taken place (involving universal/forwarding references). Consider the code sample below. Removing the std::forward would print out requires lvalue and adding the std::forward prints out requires rvalue .

Does std forward move?

C++ std::move and std::forward. C++ std::move does not move and std::forward does not forward. This article dives deep into a long list of rules on lvalues, rvalues, references, overloads and templates to be able to explain a few deceivingly simple lines of code using std::move and std::forward.

How does std:: move work?

std::move is a cast. It takes any value as argument and returns that same value in the xvalue category. And a value of type T and category xvalue is denoted thus: T&& . The move operation itself is performed by one of the constructors of the object to which it moves.


1 Answers

Ok since @vsoftco asked for concise use case here's a refined version (using his idea of having "my_forward" to actually see wich overload gets called).

I interpret "use case" by providing a code sample that without prvalue not compile or behave differently (regardless of that would be really usefull or not).

We have 2 overloads for std::forward

#include <iostream>  template <class T> inline T&& my_forward(typename std::remove_reference<T>::type& t) noexcept {     std::cout<<"overload 1"<<std::endl;     return static_cast<T&&>(t); }  template <class T> inline T&& my_forward(typename std::remove_reference<T>::type&& t) noexcept {     std::cout<<"overload 2"<<std::endl;     static_assert(!std::is_lvalue_reference<T>::value,               "Can not forward an rvalue as an lvalue.");     return static_cast<T&&>(t); } 

And we have 4 possible use cases

Use case 1

#include <vector> using namespace std;  class Library {     vector<int> b; public:     // &&     Library( vector<int>&& a):b(std::move(a)){      } };  int main()  {     vector<int> v;     v.push_back(1);     Library a( my_forward<vector<int>>(v)); // &     return 0; } 

Use case 2

#include <vector> using namespace std;  class Library {     vector<int> b; public:     // &&     Library( vector<int>&& a):b(std::move(a)){      } };  int main()  {     vector<int> v;     v.push_back(1);     Library a( my_forward<vector<int>>(std::move(v))); //&&     return 0; } 

Use case 3

#include <vector> using namespace std;  class Library {     vector<int> b; public:     // &     Library( vector<int> a):b(a){      } };  int main()  {     vector<int> v;     v.push_back(1);     Library a( my_forward<vector<int>>(v)); // &     return 0; } 

Use case 4

#include <vector> using namespace std;  class Library {     vector<int> b; public:     // &     Library( vector<int> a):b(a){      } };  int main()  {     vector<int> v;     v.push_back(1);     Library a( my_forward<vector<int>>(std::move(v))); //&&     return 0; } 

Here's a resume

  1. Overload 1 is used, without it you get compilation error
  2. Overload 2 is used, without it you get compilation error
  3. Overload 1 is used, wihtout it you get compilation error
  4. Overload 2 is used, without it you get compilation error

Note that if we do not use forward

Library a( std::move(v)); //and Library a( v); 

you get:

  1. Compilation error
  2. Compile
  3. Compile
  4. Compile

As you see, if you use only one of the two forward overloads, you basically cause to not compile 2 out of 4 cases, while if you do not use forward at all you would get to compile only 3 out of 4 cases.

like image 128
CoffeDeveloper Avatar answered Nov 03 '22 00:11

CoffeDeveloper