Reasons for std::move() and std::forward()

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

https://channel9.msdn.com/Shows/Going+Deep/Cpp-and-Beyond-2012-Scott-Meyers-Universal-References-in-Cpp11

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);
}

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.