std::move can allow the efficient transfer of resources from object to to object. Andreas Fertig reminds us that using std::move inappropriately can make code less efficient.
In this article, I try to tackle a topic that comes up frequently in my classes: move semantics, and when to use std::move. I will explain to you why you should suggest std::move yourself (in most cases). However, move semantics is way bigger than what this article covers, so don’t expect a full guide to the topic.
| Looking More Closely | |
|
The example in Listing 1 is the code I used to make my point: don’t use std::move on temporaries! Plus, in general, trust the compiler and only use std::move rarely. For this article, let’s focus on the example code.
class S {
public:
S() { printf("default constructor\n"); }
~S() { printf("deconstructor\n"); }
// Copy constructor ①
S(const S&) { printf("copy constructor\n"); }
// Move constructor ②
S(S&&) { printf("move constructor\n"); }
};
void Use()
{
S obj{
S{} // Creating obj with a temporary of S ③
};
}
|
| Listing 1 |
Here we see a, well, perfectly movable class. I left the assignment operations out. They are not relevant. Aside from the constructor and destructor, we see in ① the copy constructor and in ② the move constructor. All special members print a message to identify them when they are called.
Further down in Use, we see ③, a temporary object of S used to initialize obj, also of type S. This is the typical situation where move semantics excels over a copy (assuming the class in question has movable members). The output I expect, and I wanted to show my participants, is:
default constructor move constructor deconstructor deconstructor
However, the resulting output was:
default constructor deconstructor
Performance-wise, the output doesn’t look bad, but it doesn’t show a move construction. The question is, what is going on here?
This is the time to apply std::move, right?
At this point, somebody’s suggestion was to add std::move.
void Use()
{
S obj{
// Moving the temporary into obj
std::move(S{})
};
}
This change indeed leads to the desired output:
default constructor move constructor deconstructor deconstructor
It looks like we just found proof that std::move is required all the time. The opposite is the case! std::move makes things worse here. To understand why, let’s first talk about the C++ standard I used to compile this code.
Wait a moment!
In C++14, the output is what I showed you for both Clang and GCC. Even if we compile with -O0 that doesn’t change a thing. We need std::move to see that the move constructor is called. The key here is that the compiler can optimize the temporary away, resulting in only a single default construction. We shouldn’t see a move here because the compiler is already able to optimize it away. The best move operation will not help us here. Nothing is better than eliding a certain step. Eliding is the keyword here. To see what is going on, we need to use the -fno-elide-constructors flag, which Clang and GCC support.
Now the output changes. Running the initial code, without std::move, in C++14 mode shows the expected output:
default constructor move constructor deconstructor deconstructor
If we now switch to C++17 as the standard, the output is once again:
default constructor deconstructor
Due to the mandatory copy elision in C++17, the compiler must elide this nonsense construction even with -fno-elide-constructors. However, if we apply std::move to the temporary copy, elision doesn’t apply anymore, and we’re back to seeing a move construction.
You can verify this on Compiler Explorer: godbolt.org/z/G1ebj9Yjj
The take away
That means, hands-off! Don’t move temporary objects! The compiler does better without us.
References
[Orr18] Roger Orr, ‘Nothing is better than copy or move’ presentation given at ACCU 2018, available at: https://youtu.be/-dc5vqt2tgA
is a trainer and lecturer on C++11 to C++20, who presents at international conferences. Involved in the C++ standardization committee, he has published articles (for example, in iX) and several textbooks, most recently Programming with C++20. His tool – C++ Insights (https://cppinsights.io) – enables people to look behind the scenes of C++, and better understand constructs.
This article was published on Andreas Fertig’s blog in February 2022 (https://andreasfertig.blog/2022/02/why-you-should-use-stdmove-only-rarely/).









