when to use what?
rvalue references – use move()
universal references – use forward()
universal references
In order to understand both std::move() and std::forward() it is best to understand the idea of a universal reference. An example of a universal reference is a templated rvalue. e.g.
template<typename T>
void wrapper(T&& arg)
It will bind to any type.
Scott Meyers give a great overview of this.
https://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers
lvalue and rvalue-ness is separate from type!
If you consider the function
void fn(Widget&& w)
The type is Widget&&, but w (because it has a name) is an lvalue. NB type and lvalue/rvalue -ness are independent. So if you use w in this function you probably need to use std::move(), or the copy operations will be called.
std::move() – tell the complier to invoke a move operation
std::move() returns a rvalue reference and this binds to the move operation.
template<typename T>
remove_references(T)&& move(T&& t)
move() uses a universal reference to bind to anything and returns an r-value, with any extra &&’s removed.
It is equivalent to doing a static_cast to the r-value.
In full the prototype is:
template< class T >
constexpr typename std::remove_reference::type&& move(T&& t) noexcept;
And an example of the code is:
template inline
typename std::remove_reference::type&&
move(_Ty&& _Arg)
{
return ((typename std::remove_reference::type&&)_Arg);
}
std::forward() – tell the complier to do perfect forwarding
std::forward() is defined as
template<typename T> T&& forward(remove_references(T)&& t) – (1)
template<typename T> T&& forward(remove_references(T)& t) - (2)
1 – This overload makes it possible to forward a result of an expression (such as function call), which may be rvalue or lvalue, as the original value category of a forwarding reference argument.
However it does not do the correct thing for lvalue references hence we need the overload 2.
2 – Forwards lvalues as lvalues references.
The lvalue binds as a lvalue reference.. The template parameter is thus something like Widget&, and the return value Widget& && => Widget& (via reference collapsing rules) which is what is returned. [Editors note! This is what I understand is happening, but maybe I need to revisit this later!]
how forward is used
The forward() method is used in code like this.
template<class T>
void wrapper(T&& arg)
{
foo(arg);
}
Here we want arg forwarded as the same type it was bound. Here arg is an lvalue reference (as it has a name). This is where forward() comes to the rescue. forward() converts arg back to the original type.
template<class T>
void wrapper(T&& arg)
{
foo(forward(arg));
}
In full the prototype of forward is:
template< class T >
constexpr T&& forward(typename std::remove_reference::type& t) noexcept;
template< class T >
constexpr T&& forward(typename std::remove_reference::type&& t) noexcept;
And an example of the code is:
template <class T>
inline T&& forward(typename std::remove_reference<T>::type& t) noexcept
{
return static_cast<T&&>(t);
}
template <class T>
inline T&& forward(typename std::remove_reference<T>::type&& t) noexcept
{
static_assert(!std::is_lvalue_reference<T>::value,
“Can not forward an rvalue as an lvalue.”);
return static_cast<T&&>(t);
}