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.
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 .
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.
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.
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
Note that if we do not use forward
Library a( std::move(v)); //and Library a( v);
you get:
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With