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