194

I saw this here: Move Constructor calling base-class Move Constructor

Could someone explain:

  1. the difference between std::move and std::forward, preferably with some code examples?
  2. How to think about it easily, and when to use which
Patryk
  • 22,602
  • 44
  • 128
  • 244
aCuria
  • 6,935
  • 14
  • 53
  • 89
  • 3
    See also these two related questions: [Should I use std::move or std::forward in move ctors/assignment operators?](http://stackoverflow.com/q/8860233/500104) and [How does std::forward work?](http://stackoverflow.com/q/8526598/500104) (aswell as the duplicated question). – Xeo Mar 12 '12 at 17:34
  • 2
    "How to think about it easily, and when to use which" You use `move` when you want to *move* a value, and `forward` when you want to use perfect forwarding. This isn't rocket science here ;) – Nicol Bolas Mar 12 '12 at 17:47
  • 1
    move() perform unconditional cast where as forward() perform cast based on the parameter that has passed. – RaGa__M May 18 '16 at 12:24

4 Answers4

184

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.

Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
  • I'm not sure it's accurate that `std::forward`'s _only_ use case is perfect forwarding of function arguments. I've run into situations where I want to perfectly forward other things, like object members. – Geoff Romer Dec 11 '13 at 16:53
  • @GeoffRomer Members of a parameter? Do you have an example? – Potatoswatter Dec 11 '13 at 23:11
  • I posted an example as a separate question: http://stackoverflow.com/questions/20616958/stdforward-without-perfect-forwarding – Geoff Romer Dec 16 '13 at 23:47
  • 1
    Hmm, so if I understand this correctly, it means that I can write a single function forward() where forward(5) operates on 5 as if I passed it by value where forward(x) operates on x as if I passed it by reference without having to explicitly write an overload. – iheanyi Jul 17 '14 at 21:07
  • 1
    @iheanyi Yep, that's the idea! But it has to be a template, not just an ordinary function. – Potatoswatter Jul 17 '14 at 23:53
  • Thanks. I did some more reading and I think I understand the use with templates. Your answer did a lot to help me wrap my head around the concept :) – iheanyi Jul 18 '14 at 14:35
  • If std::forward passes on the rvalue to overloaded, I could imagine overloaded to invalidate the object because it assumes the object was moved. How is std::forward avoiding this situation? – Maximilian Mordig Jul 25 '16 at 16:01
  • @MaximilianMordig You are correct. Only pass an object to `std::forward` if it is ready to be invalidated in the rvalue case. – Potatoswatter Jul 27 '16 at 00:09
  • A slightly tangential, noob question: since fowarding takes t&&, it means it accepts rvalues for that parameter. Why is forwarding(x) valid then, since it's passing an lvalue? – Pratik Nov 30 '18 at 23:17
  • @Pratik when you declaring an expression as `T&& expression;`, it could be either rvalue or lvalue. If `expression` has a name, it is lvalue; Otherwise, it is rvalue. So if you have `Class C{ C(const C&){} C(C&&){} }; void Func( C&& arg ){ C a(arg); }`, The copy constructor `C(const C&)` will be called. Due to the argument `arg` is lvalue. – r0n9 Feb 07 '19 at 04:03
  • Why does `std::forward` require you to explicitly specify the template parameter instead of deducing it like `std::move` does? – PieterNuyts Dec 18 '19 at 13:44
68

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.

Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577
  • If I perfectly forward an object to a function using `std::forward`, then after that function is executed, can I use that object? I am aware that, in case of `std::move`, it's an undefined behavior. – iammilind Dec 26 '17 at 11:32
  • 1
    Regarding `move`: https://stackoverflow.com/a/7028318/576911 For `forward`, if you pass in an lvalue, your API should react as if it receives an lvalue. Normally this means the value will be unmodified. But if it is a non-const lvalue, your API may have modified it. If you pass in an rvalue, this normally means that your API may have moved from it, and thus https://stackoverflow.com/a/7028318/576911 would apply. – Howard Hinnant Dec 26 '17 at 15:29
21

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.

Community
  • 1
  • 1
Bo Persson
  • 90,663
  • 31
  • 146
  • 203
18

I think comparing two example implementations can provide a lot of insight on what they are for and how they differ.

Let's start with std::move.

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:

  • it is a template function, so it works on any type T;
  • it takes its sole parameter via universal (or forwarding) reference 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;
  • template type deduction is in place, so you don't have to specify the template argument via <…>, and, in practice, you should never specify it;
  • this also means that 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;
  • it returns an rvalue without making any copy, by using an rvalue reference type (instead of a non-reference type) for the return type; it does so by stripping any reference-ness from T, via std::remove_reference_t, and then adding &&.

Trivia

Do 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:

  • (if 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;
  • (if 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:

  • it is a template function, so it works on any type T;
  • it takes its sole parameter via lvalue reference to a reference-less 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...
  • ... the reason why 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 <…>
  • this also means that 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;
  • it returns an rvalue or an lvalue without making any copy, by using an rvalue reference or lvalue reference type (instead of a non-reference type) for the return type; it does so by relying on reference collapsing applied to 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;
  • some people writes static_cast<T&&> instead of std::forward<T>, because that's what it is, and they save an instatiation, at the expense of less clear code and possibility to not catch bugs.

¹ Scott Meyers in Effective Modern C++ says precisely the following:

std::move unconditionally casts its argument to an rvalue

Enlico
  • 23,259
  • 6
  • 48
  • 102
  • _"Long story short: std::move is for turning anything into an rvalue"_. That is not 100% correct. It cannot turn _anything_ into an rvalue (which can be moved, or stolen from). If the argument has constness in it, then `std::move` wont change that fact, and then you wont be able to move the result. – Nawaz Sep 24 '21 at 13:22
  • @Nawaz, from [here](https://en.cppreference.com/w/cpp/language/value_category) and [here](https://en.cppreference.com/w/cpp/utility/move) it appears to me that `std::move(whatever)` is a xvalue, hence an rvalue. Whether that rvalue is `const` or not is another story. And it has its consequences as you suggest (can't be moved from). But I think my statement is correct nonetheless. – Enlico Sep 24 '21 at 16:14
  • @Nawaz, `const` is part of the _type_, which is a property (of an expression) independent from the _value category_ property (see first paragraph [here](https://en.cppreference.com/w/cpp/language/value_category)). Therefore, I think my statement is 100% correct, as I didn't write _turn anything into a **modifiable** rvalue_. – Enlico Sep 25 '21 at 09:32
  • @Nawaz, here's a citation. – Enlico Sep 25 '21 at 09:36
  • If I take your statement completely, then it's wrong. As you said (see the emphasis): _"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 it's resources can be stolen from it (i.e. moved from it)**."_ So it seems you're under the impression that `std::move()` will make the argument _steal-able_ no matter what – Nawaz Sep 27 '21 at 11:07
  • That's not what you commented initially. However, I'll add a couple of words. – Enlico Sep 27 '21 at 11:08
  • 1
    That is my comment. See it carefully. I didn't quote it completely, as it's verbose and comment has limit on number of characters. I simply rephrased it in the braces "(which can be moved, or stolen from)" which you probably ignored. – Nawaz Sep 27 '21 at 11:09
  • Good, added some words now. – Enlico Sep 27 '21 at 11:11
  • 1
    Looks great now! Thanks for the edit. Upvoted. :-) – Nawaz Sep 27 '21 at 11:17
  • Thank you for prompting me to improve it. And yes, I didn't notice that piece in parenthesis of your first comment. – Enlico Sep 27 '21 at 11:19
  • Great! move casts the parameter to an rvalue; forward forwards the parameter as it is (unchanged) received either it’s an lvalue or an rvalue, so that the last function that receives the forwarded value can be aware of what to or not to do with the received value. – Maf Mar 21 '23 at 22:42