Your handy cut-out-and-keep guide to std::forward and std::move

I love a good ‘quadrant’ diagram.  It brings me immense joy if I can encapsulate some wisdom, guideline or rule-of-thumb in a simple four-quadrant picture.

This time it’s the when-and-where of std::move and std::forward.  In my experience, when programmers are first introduced to move semantics, their biggest struggle is to know when (or when not) to apply std::move or std::forward.  Usually, it’s a case of “keep apply std::move until it compiles”.  I’ve been there myself.

To that end I’ve put together a couple of a simple overview quadrant graphics to help out the neophyte ‘mover-forwarder’.  The aim is to capture some simple rules-of-thumb in an easy-to-digest format.

Disclaimer:  these diagrams don’t address every move/forwarding use.   They’re not intended to.  That’s why we have books, presentations and long rambling articles on the topic.

You’re writing a function…

You want to know whether you should use std::move or std::forward on the parameter(s)

Example 1:  A non-template function with an l-value reference parameter.

You don’t want to use std::move or std::forward because the client should expect their object to be modified – but not expired – after the call.

void func(ADT& param)
{
  ADT temp = param;  // Copy object
}

 

Example 2: A template function with an l-value reference parameter

As above, but this time the type of the parameter is (probably) deduced.

template <typename T>
void func(T& param)
{
  T temp = param;  // Copy object
}

 

Example 3: A non-template function with an r-value reference parameter

Overloading with an r-value reference parameter is an explicit declaration that you intend to move from the parameter.

void func(ADT&& param)
{
  ADT temp = std::move(param);  //  See rules below
}

 

Example 4: A template function with an r-value reference parameter

This is the Forwarding Reference Idiom.   Your function is forwarding-on its parameter to some underlying function.  Typically, you want to pass that argument on without changing its value category.

template <typename T>
void wrapper_func(T&& param)
{
  utility_func(std::forward<T>(param));    // l-values are copied
                                           // r-values are moved
}

 

You’re calling a function…

You want to know whether you should use std::move or std::forward on the argument.  By the way, you can apply this set of rules to returning parameters from functions, too.

 

Example 1: The expression is an l-value; you need the object after the call.

In other words, a typical function call!

int main()
{
  ADT adt { };

  func(adt);

  // use adt again...
}

 

Example 2:  The expression is an r-value.

The object will be destroyed after the call, so you can’t retain it.  There is no need to call std::move on an object that is already an r-value expression.

int main()
{
  func(ADT { });  // This object is just for the call
}

 

Example 3: The expression is an l-value; you don’t need the object after the call.

The object will be an x-value (expired) object after the call.

int main()
{
  ADT adt { };

  func(std::move(adt));

  // adt shouldn’t be used again...
}

 

Want to know more?…

If you want to explore this in more detail have a read here, here, here and even here

Glennan Carnie
Dislike (0)
Website | + posts

Glennan is an embedded systems and software engineer with over 20 years experience, mostly in high-integrity systems for the defence and aerospace industry.

He specialises in C++, UML, software modelling, Systems Engineering and process development.

About Glennan Carnie

Glennan is an embedded systems and software engineer with over 20 years experience, mostly in high-integrity systems for the defence and aerospace industry. He specialises in C++, UML, software modelling, Systems Engineering and process development.
This entry was posted in C/C++ Programming and tagged , , , , , , , . Bookmark the permalink.

2 Responses to Your handy cut-out-and-keep guide to std::forward and std::move

  1. Titus Winters says:

    A function that has an rvalue-ref parameter isn't a declaration that it will be moved from, only that it may be moved from. (Probably different if it's in an overload set.)

    Like (2)
    Dislike (0)
  2. Kranti says:

    I suppose yours is one of the best C++ sites till now...it's in top 10 for sure...

    Like (3)
    Dislike (0)

Leave a Reply