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
https://cppinsights.io) – enables people to look behind the scenes of C++, and better understand constructs.
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 (This article was published on Andreas Fertig’s blog in February 2022 (https://andreasfertig.blog/2022/02/why-you-should-use-stdmove-only-rarely/).