std::move and std::forward
std::move
std::move does'nt move anything and std::forward does'nt forward anything.- std::move unconditionally casts its arguments to an rvalue
- std::forward performs this casts only if a particular condition is fulfilled.
Sample implementation of std::move below
template<typename T>
typename remove_reference<T>::type&& move(T param)
{
using ReturnType = typename remove_reference<T>::type&&;
return static_cast<ReturnType> (param);
}
std::move takes a reference to an object and it returns a reference to the same object.
The "&&" part of the functions return type implies that std::move returns an rvalue reference, but if the type T happens to be an lvalue reference, T&& would become an lvalue reference. To prevent this from happening, the type trait remove_reference is applied to T, thus ensuring that "&&" is applied to a type isn;t a reference. That guarantees that std::move truly returns an rvalue reference, and that's important, because rvalues returned from functions are rvalues. This std::move casts its arguments to rvalues.
Few steps to be taken before applying move to any data member.
- First, don't declare objects const if you want to be able to move from them. Move requests on const objects are silently transformed into copy operations.
- Second, std::move not only does'nt actually move anything, it does'nt even guarantee that the obejct it's casting will be eligible to be moved. The only thing you know for sure about the result of applying std::move to an object is that it's an rvalue.
std::forward
std::forward is smilar to that of std::move, but whereas std::move uncondinionally casts its arguments to an rvalue, std::foward does it under som conditions. std::forward is an conditional cast.
Most common scenario is a function template taking a universal reference parameter that is passed to another function.
void process(const Widget& lvalue); // process lvaues
void process(Widget&& rvalue); // process rvalues
template <typename T>
void callProcess(T&& param)
{
process(std::forward<T>(param));
}
Consider two calls to callProcess, one with lvalue and other with rvalue:
Widget w;
callProcess(w); // call with lvalue
callProcess(std::move(w)); // call with rvalue
Inside callProcess, the parameter param is passed to the function process. process is overloaded for lvaues and rvalues. When we call callProcess with an lvalue, we naturally expect that lvalue to be forwarded to process as an lvalue, and when we call callProcess with an rvalue, we expect the rvalue overload of process be invoked.
But param, like all function parameters, is an lvalue. Every call to process inside callProcess will thus want to invoke the lvalue overload for process. To prevent this, we need the mechanism for param to be cast to an rvalue if and only if the argument with which param was initialized - the argument passed to callProcess was an rvalue. std::forward is a conditional cast : it casts to an rvalue only if its argument was initialised with an rvalue.
But how std::forward can know whether its argument was initialised with an rvalue. The answer is that information is encoded in callProcess's template parameter T. Parameter is passed to std::forward, which recovers the encoded information.
Both std::move and std::forward boils down to casts, the only difference being that std::move always casts, while std::forward only sometime casts
std::move requires only a function argument (rhs.s), while std::forward requires both a function argument and a template type argument (std::string).
- std::move sets up a move.
- std::forward just passes - forwards - an object to another function in a way that retains its original lvalueness and rvalueness.
- std::move performs an unconditional cast to an rvalue
- std::forward casts its arguments to an rvalue only if that argument is bound to an rvalue.
Comments
Post a Comment