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
- Practice makes perfect, part 3 – Idiomatic kata - February 27, 2020
- Practice makes perfect, part 2– foundation kata - February 13, 2020
- Practice makes perfect, part 1 – Code kata - January 30, 2020
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.


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.)
I suppose yours is one of the best C++ sites till now...it's in top 10 for sure...