C++ std::move
Table of Contents
std::move #
It really is just a cast to T&&
Some code #
#include <iostream>
#include <string>
class Widget {
public:
Widget() = default;
void set(const std::string & name_) {
std::cout << "const ref " << name_ << std::endl;
name = name_;
std::cout << "const ref " << name << std::endl;
}
void set(std::string && name_) {
std::cout << "r-ref " << name_ << std::endl;
name = std::move(name_);
std::cout << "r-ref " << name << std::endl;
}
private:
std::string name;
};
int main() {
auto w = Widget{};
w.set(std::string{"first"});
std::string other { "other"};
w.set(other);
w.set(std::move(other));
std::cout << "Other = '" << other << "'\n";
return 0;
}
Output:
r-ref first
r-ref first
const ref other
const ref other
r-ref other
r-ref other
Other = ''
Observations #
If I change the second set
method to:
void set(std::string && name_) {
std::cout << "r-ref " << name_ << std::endl;
name = name_;
std::cout << "r-ref " << name << std::endl;
}
we get:
r-ref first
r-ref first
const ref other
const ref other
r-ref other
r-ref other
Other = 'other'
If we make other
const:
int main() {
auto w = Widget{};
w.set(std::string{"first"});
const std::string other { "other"};
w.set(other);
w.set(std::move(other));
std::cout << "Other = '" << other << "'\n";
return 0;
}
we get:
r-ref first
r-ref first
const ref other
const ref other
const ref other
const ref other
Other = 'other'
and clang-tidy
, if installed, warns:
Clang-Tidy: Std::move of the const variable ‘other’ has no effect; remove std::move() or make the variable non-const
because, under standard C++ lookup rules, it cannot find a method with the signature void set(const std::string&& name_);
so it defers to the const reference version,
but if we add such a method:
void set(const std::string && name_) {
std::cout << "const r-ref " << name_ << std::endl;
name = std::move(name_);
std::cout << "const r-ref " << name << std::endl;
}
Which works, sort of:
r-ref first
r-ref first
const ref other
const ref other
const r-ref other
const r-ref other
Other = 'other'
Note the last line, other
has not been moved by std::string
. That’s because std::string
does not have any members that take a const T&&
.
In fact, moving implies modification, so it makes little sense to create methods that take const T&&
.
More links here:
- https://www.codesynthesis.com/~boris/blog/2012/07/24/const-rvalue-references/
- https://stackoverflow.com/questions/24824432/what-is-use-of-the-ref-qualifier-const
- https://stackoverflow.com/questions/28595117/why-can-we-use-stdmove-on-a-const-object/28595207
Moving #
We ought to use std::exchange
to safely move, and also ‘invalidate’ the moved-from object with a sensible value:
class Widget {
Gizmo* gizmo_{};
Value value_{};
public:
Widget(Widget&& other)
: gizmo_(std::exchange(other.gizmo_, nullptr)),
value_(std::exchange(other.value_, {})) {}]
};