std::move takes an object and casts it as an rvalue reference, which indicates that resources can be "stolen" from this object. std::forward has a single use-case: to cast a templated function parameter of type forwarding reference ( T&& ) to the value category ( lvalue or rvalue ) the caller used to pass it.
std::forward This is a helper function to allow perfect forwarding of arguments taken as rvalue references to deduced types, preserving any potential move semantics involved.
std::move is actually just a request to move and if the type of the object has not a move constructor/assign-operator defined or generated the move operation will fall back to a copy.
In C++11, std::move is a standard library function that casts (using static_cast) its argument into an r-value reference, so that move semantics can be invoked. Thus, we can use std::move to cast an l-value into a type that will prefer being moved over being copied. std::move is defined in the utility header.
std::move
takes an object and allows you to treat it as a temporary (an rvalue). Although it isn't a semantic requirement, typically a function accepting a reference to an rvalue will invalidate it. When you see std::move
, it indicates that the value of the object should not be used afterwards, but you can still assign a new value and continue using it.
std::forward
has a single use case: to cast a templated function parameter (inside the function) to the value category (lvalue or rvalue) the caller used to pass it. This allows rvalue arguments to be passed on as rvalues, and lvalues to be passed on as lvalues, a scheme called "perfect forwarding."
To illustrate:
void overloaded( int const &arg ) { std::cout << "by lvalue\n"; }
void overloaded( int && arg ) { std::cout << "by rvalue\n"; }
template< typename t >
/* "t &&" with "t" being template param is special, and adjusts "t" to be
(for example) "int &" or non-ref "int" so std::forward knows what to do. */
void forwarding( t && arg ) {
std::cout << "via std::forward: ";
overloaded( std::forward< t >( arg ) );
std::cout << "via std::move: ";
overloaded( std::move( arg ) ); // conceptually this would invalidate arg
std::cout << "by simple passing: ";
overloaded( arg );
}
int main() {
std::cout << "initial caller passes rvalue:\n";
forwarding( 5 );
std::cout << "initial caller passes lvalue:\n";
int x = 5;
forwarding( x );
}
As Howard mentions, there are also similarities as both these functions simply cast to reference type. But outside these specific use cases (which cover 99.9% of the usefulness of rvalue reference casts), you should use static_cast
directly and write a good explanation of what you're doing.
Both std::forward
and std::move
are nothing but casts.
X x;
std::move(x);
The above casts the lvalue expression x
of type X to an rvalue expression of type X (an xvalue to be exact). move
can also accept an rvalue:
std::move(make_X());
and in this case it is an identity function: takes an rvalue of type X and returns an rvalue of type X.
With std::forward
you can select the destination to some extent:
X x;
std::forward<Y>(x);
Casts the lvalue expression x
of type X to an expression of type Y. There are constraints on what Y can be.
Y can be an accessible Base of X, or a reference to a Base of X. Y can be X, or a reference to X. One can not cast away cv-qualifiers with forward
, but one can add cv-qualifiers. Y can not be a type that is merely convertible from X, except via an accessible Base conversion.
If Y is an lvalue reference, the result will be an lvalue expression. If Y is not an lvalue reference, the result will be an rvalue (xvalue to be precise) expression.
forward
can take an rvalue argument only if Y is not an lvalue reference. That is, you can not cast an rvalue to lvalue. This is for safety reasons as doing so commonly leads to dangling references. But casting an rvalue to rvalue is ok and allowed.
If you attempt to specify Y to something that is not allowed, the error will be caught at compile time, not run time.
std::forward
is used to forward a parameter exactly the way it was passed to a function. Just like shown here:
When to use std::forward to forward arguments?
Using std::move
offers an object as an rvalue, to possibly match a move constructor or a function accepting rvalues. It does that for std::move(x)
even if x
is not an rvalue by itself.
I think comparing two example implementations can provide a lot of insight on what they are for and how they differ.
I'll start with std::move
, which I truly understood long before understanding std::forward
.
std::move
Long story short: std::move
is for turning anything into an rvalue(¹), for the purpose of making it look like a temporary (even if it isn't: std::move(non_temporary)
), so that its resources can be stolen from it, i.e. moved from it (provided this is not prevented by a const
attribute; yes, rvalues can be const
, in which case you can't steal resources from them).
std::move(x)
says Hi guys, be aware that who I'm giving this x
to can use and break it apart as he likes, so you typically use it on rvalue references parameters, because you're sure they are bound to temporaries.
This is a C++14 implementation of std::move
very similar to what Scott Meyers shows in Effective Modern C++ (in the book the return type std::remove_reference_t<T>&&
is changed to decltype(auto)
, which deduces it from the return
statement)
template<typename T>
std::remove_reference_t<T>&& move(T&& t) {
return static_cast<std::remove_reference_t<T>&&>(t);
}
From this we can observe the following about std::move
:
T
;T&&
, so it can operate on both lvalues and rvalues; T
will correspondingly be deduced as an lvalue reference or as a non-reference type;<…>
, and, in practice, you should never specify it;std::move
is nothing more than a static_cast
with the template argument automatically determined based on the non-template argument, whose type is deduced;T
, via std::remove_reference_t
, and then adding &&
.Did you know that, beside the std::move
from <utility>
that we are talking about, there's another one? Yeah, it's std::move
from <algorithm>
, which does a mostly unrelated thing: it's a version of std::copy
which, instead of copying values from one container to another, it moves them, using std::move
from <utility>
; so it is a std::move
which uses the other std::move
.
std::forward
Long story short: std::forward
is for forwarding an argument from inside a function to another function while telling the latter function whether the former was called with a temporary.
std::forward<X>(x)
says one of two things:
x
is bound to an rvalue, i.e. a temporary) Hi Mr Function, I've received this parcel from another function who doesn't need it after you work with it, so please feel free to do whatever you like with it;x
is bound to an lvalue, i.e. a non-temporary) Hi Mr Function, I've received this parcel from another function who does need it after you work with it, so please don't break it.So you typically use it on forwarding/universal references, because they can bind to both temporaries and non temporaries.
In other words, std::forward
is for being able to turn this code
template<typename T>
void wrapper(T&& /* univ. ref.: it binds to lvalues as well as rvalues (temporaries)*/ t) {
// here `t` is an lvalue, so it doesn't know whether it is bound to a temporary;
// `T` encodes this missing info, but sadly we're not making `some_func` aware of it,
// therefore `some_func` will not be able to steal resources from `t` if `t`
// is bound to a temporary, because it has to leave lvalues intact
some_func(t);
}
into this
template<typename T>
void wrapper(T&& /* univ. ref.: it binds to lvalues as well as rvalues (temporaries)*/ t) {
// here `t` is an lvalue, so it doesn't know whether it is bound to a temporary;
// `T` encodes this missing info, and we do use it:
// `t` bound to lvalue => `T` is lvalue ref => `std::forward` forwards `t` as lvalue
// `t` bound to rvalue => `T` is non-ref => `std::forward` turns `t` into rvalue
some_func(std::forward<T>(t));
}
This is the C++14 implementation of std::forward
from the same book:
template<typename T>
T&& forward(std::remove_reference_t<T>& t) {
return static_cast<T&&>(t);
}
From this we can observe the following about std::forward
:
T
;T
; note that, because of Reference collapsing (see here), std::remove_reference_t<T>&
resolves exactly to the same thing as T&
would resolve to; however...std::remove_reference_t<T>&
is used instead of T&
is exactly to put T
in a non-deduced context (see here), thus disabling template type deduction, so that you are forced to specify the template argument via <…>
std::forward
is nothing more than a static_cast
with the template argument automatically determined (via reference collapsing) based on the template argument that you must pass to std::forward
;T&&
, where T
is the one that you passed as template argument to std::forward
: if that T
is a non-reference, then T&&
is an rvalue reference, whereas if T
is an lvalue reference, then T&&
is an lvalue reference too.¹ Scott Meyers in Effective Modern C++ says precisely the following:
std::move
unconditionally casts its argument to an rvalue
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