This answer is for answering comment by @vsoftco
@DarioOO thanks for the link. Can you maybe write a succinct answer? From your example it's still not clear for me why does std::forward need to be also defined for rvalues
In short:
Because without a rvalue specialization the following code would not compile
#include <utility>
#include <vector>
using namespace std;
class Library
{
vector<int> b;
public:
// hi! only rvalue here :)
Library( vector<int>&& a):b(std::move(a)){
}
};
int main()
{
vector<int> v;
v.push_back(1);
A a( forward<vector<int>>(v));
return 0;
}
however I can't resist to type more so here's also the not succint version of the answer.
Long version:
You need to move v
because the class Library
has no constructor accepting lvalue to it, but only a rvalue reference.
Without perfect forwarding we would end up in a undesired behaviour:
wrapping functions would incurr high performance penality when passing heavy objects.
with move semantics we make sure that move constructor is used IF POSSIBLE.
In the above example if we remove std::forward
the code will not compile.
So what is actually doing forward
? moving the element without our consensus? Nope!
It is just creating a copy of the vector and moving it. How can we be sure about that? Simply try to access the element.
vector<int> v;
v.push_back(1);
A a( forward<vector<int>>(v)); //what happens here? make a copy and move
std::cout<<v[0]; // OK! std::forward just "adapted" our vector
if you instead move that element
vector<int> v;
v.push_back(1);
A a( std::move(v)); //what happens here? just moved
std::cout<<v[0]; // OUCH! out of bounds exception
So that overload is needed to make possible a implicit conversion that is still safe, but not possible without the overload.
Infact the following code will just not compile:
vector<int> v;
v.push_back(1);
A a( v); //try to copy, but not find a lvalue constructor
Real use case:
You may argue that forwarding arguments may create useless copies and hence hide a possible performance hit, yes, that's actually true, but consider real use cases:
template< typename Impl, typename... SmartPointers>
static std::shared_ptr<void>
instancesFactoryFunction( priv::Context * ctx){
return std::static_pointer_cast<void>( std::make_shared<Impl>(
std::forward< typename SmartPointers::pointerType>(
SmartPointers::resolve(ctx))...
) );
}
Code was taken from my framework (line 80): Infectorpp 2
In that case arguments are forwarded from a function call. SmartPointers::resolve
's returned values are correctly moved regardless of the fact that constructor of Impl
accept rvalue or lvalue (so no compile errors and those get moved anyway).
Basically you can use std::foward
in any case in wich you want to make code simpler and more readable but you have to keep in mind 2 points
- extra compile time (not so much in reality)
- may cause unwanted copies (when you do not explicitly move something into something that require a rvalue)
If used with care is a powerfull tool.