6 Move Operation
Published:
Motivation
Construct a local array and return by value
- Will invoke 2 copy constructions
IntArray createIntArray() {
IntArray tmp;
for (int i = 0; i < 20; i++) {
tmp.push_back(i);
std::cout << tmp << std::endl;
}
return tmp; // copy by value
}
int main() {
IntArray ia { createIntArray() }; // make another copy
}
Earlier,
deleted the copy constructor!
It’s very cumbersome to return an object by value by copying!
- Want to pass a reference, and allocate the structure outside the function
void createIntArray(IntArray* ia_ptr) {
for (int i = 0; i < 20; i++) {
ia_ptr->push_back(i);
std::cout << *ia_ptr << std::endl;
}
}
int main() {
IntArray ia; // allocate locally on the stack
createIntArray(&ia);
}
Move Constructor
Move constructor only kicks in when assigning to a temporary object that will go away
- More efficient than copy
- Do a shallow copy of each element from
tmpto the new objectthis - Sever the connection to the original pointer
.a
IntArray(IntArray&& tmp) : sz{tmp.sz}, cap{tmp.cap}, a{tmp.a} {
tmp.sz = tmp.cap = 0;
tmp.a = nullptr;
std::cout << "move ctor" << std::endl;
}
Instead of making a deep copy of original
tmp, “steals” whattmpused to have, and move to the return
Now, when tmp -> unnamed return -> ia, each old object gets destroyed
- Instead of making a brand object and making a copy, steal the internals
- Two copies becomes two movements
- Old object gets destroyed, and
delete nullptris okay
Move constructor can be elided as well
&& rvalue Reference
- Regular variables are lvalue
int i = 5,ican be on the LHS, lvalue5can’t be lvalue, can’t do5 = i
MyString{"xyz"}is rvalue
struct X {
X() : d{100} {} // set X.d to 100
double d;
};
void f1(X& t) { t.d *= 2; cout << t.d << endl; }
void f2(const X& t) { cout << "can't change t" << endl; }
void f3(X&& t) { t.d *= 3; cout << t.d << endl; }
int main() {
X x; // x is an lvalue
f1(x); // passing an lvalue to X& --> ok
f2(x); // passing an lvalue to const X& --> ok
f3(x); // passing an lvalue to X&& --> not ok
// X{} creates a temp object (rvalue)
f1( X{} ); // passing an rvalue to X& --> not ok
f2( X{} ); // passing an rvalue to const X& --> ok
f3( X{} ); // passing an rvalue to X&& --> ok
}
- Copy constructor take
constreference, since old object not modified - Move operator take revalue reference to bind to rvalue
- Only allow stealing from temporary objects
- Not named!!!
Move Assignment
IntArray& operator=(IntArray&& tmp) {
if (this != &tmp) {
delete[] a;
sz = tmp.sz;
cap = tmp.cap;
a = tmp.a;
tmp.sz = tmp.cap = 0;
tmp.a = nullptr;
}
std::cout << "move assignment" << std::endl;
return *this;
}
Usage
IntArray ia = { createIntArray() }
IntArray ia2;
ia2 = ia; // we don't have copy assignment,
Won’t work since ia is an lvalue, not allowed to move!!
Need to cast to force a move assignment
ia2 = (IntArray&&) ia; // not recommended
// or
ia2 = std::move(ia);
Original ia loses its content
Summary
Compiler can generate a move constructor and move assignment
There are some edge cases for that, beyond the scope
- When no destructor, copy constructor, or copy assignment declared
- Compatibility with copy rules, if legacy code write copy but not move
- Move will fall back to copy
- rvalues can be bound to
constlvalues
- rvalues can be bound to
- Member-wise move
- If either move constructor or move assignments are declared, compiler will implicitly delete the copy constructor and copy assignment
Rule of 5
- If you declare any of destructor/copy/move (including
defaultanddelete), very likely need to declare all of them
Rule of 0
- If all class members already have their 5 defined correctly (E. library class), and you class has no special resource management, no need to declare in your class
- Some prefer to declare with
=default
