This is my learning notes from the course - Mastering C++ Standard Library Features.
Move Semantics in the Standard Library
- Many classes in the standard library are
move aware
- Some classes represent
unique ownership
and are move-only
1 | struct foo {}; |
Every container in C++ is move-aware
in one way or another.
1 | void containers() |
Some classes provided by the Standard Library can only
be moved.
1 | void move_only() |
Avoiding Unnecessary Performance Hits with std::move
- Moving objects into containers
- Moving containers instead of copying
- Accepting
rvalue references
as function arguments
1 |
|
What is wrong with the code above? How can we improve it?
1 | files.push_back(std::move(s)); // moving instead of copying |
Moving containers instead of copying:
1 | void moving_containers_instead_of_copying() |
What is wrong with the code above? How can we improve it?
1 | for(int i = 0; i < 100; i++) |
Also, for loop can be reduced to as follow:
1 | for(int i = 0; i < 100; i++) |
Pass-by-Value Move Idiom
Let’s see an example:
1 | void bar(const std::vector<int>& v); // pass by const lvalue reference (copy) |
Providing this flexibility in your functions and classes is always a good thing for your users because they will always get optimal performance if they have an lvalue or an rvalue. Unfortunately, it is very cumbersome to provide multiple overloads for everything, this is why the pass-by-value move idiom
exists.
Before C++11, it was common to take sink arguments as const&
and copy.
1 |
|
Since C++11, we can support move operations for sink arguments to prevent unnecessary copies.
1 |
|
The above code is optimal for the users of person
:
- If they pass an
lvalue
, it will be copied. - If they pass an
rvalue
, it will be moved.
This quickly gets out of hand, as we need to write 2N overloads for N arguments!
Fortunately, the pass-by-value and move
idiom comes to our rescue.
1 |
|
By taking sink arguments by value
and then unconditionally moving them, we can achieve close-to-optimal behavior:
- Lvalue => 1 copy + 1 move
- Rvalue => 2 moves
Since moves are cheap, the extra move is worth not having to provide 2N overloads.
Recap
Avoiding unnecessary performance hits with std::move
Sink arguments
are objects that are going to be retained/consumed- Providing an optimal interface for
sink arguments
requires 2N overloads for N arguments (all possible combinations of const& and &&) - The
pass-by-value and move
idiom is easy to implement and maintain, and achieves close-to-optimal performance, thanks tomove semantics
.
Summary
- Learned that many classes in the standard library are move-aware or move-only
- Studied that using
std::move
can provide performance benefits and allows us to work with move-only classes - Understood that when taking a
sink argument
, use thepass-by-value and move
idiom