CS106L(14): Lovebird & Move Semantics

Lovebird is definitely cute, but a bit noisy:

2020_11_27_1

They wanna get out of the cage every day:

2020_11_27_2

Let’s learn the Move Semantics to help them.

Core Idea

Move Semantics: get existing resource without wasting new memory. Make C++ more efficient.

  • lvalues vs. rvalues
  • Move implementation
  • Forcing a move to occur: std::move()
  • swap and insert

Lecture 14: Move Semantics - Note

View Lecture Note

lvalues vs. rvalues

Use move constructor on the first line becuase it was temporary.

A move constructor of class T is a non-template constructor whose first parameter is T&&, const T&&, volatile T&&, or const volatile T&&, and either there are no other parameters, or the rest of the parameters all have default values.

int main() {
 vector<string> words1 = findAllWords(12345);  // move constructor
 vector<string> words2 = words1;   // copy assignment
}
  • L-values and r-values generalize the idea of “temporariness.”
  • An r-value is “temporary,” and an l-value is not.

r-value: tempoRaRy, or Rubbish value, so can be thrown away, l-value: long-lasting value, so can buy house in the city, so it has mailing address.

  • Official definition: a l-value has an address (can do &), and a r-value does not.

  • An l-value can appear left or right of =.

  • An r-value can only appear right of =.

Which of these are r-values?

int val = 2; // r-value
int* ptr = 0x02248837; // r-value
vector<int> v1{1, 2, 3}; // r-value
auto v4 = v1 + v2; // r-value
auto v5 = v1 += v4; // l-value
size_t size = v.size(); // r-value
val = static_cast<int>(size); // r-value
v1[1] = 4*i; // r-value
ptr = &val; // r-value
v1[2] = *ptr; // l-value
  • A l-value’s lifetime is until end of scope.
  • A r-value’s lifetime is until end of line.
  • unless it is artificially extended.

r-value Reference

r-value cannot be referenced using &

Error! Cannot pass a literal value by reference! Will have Compiler Error.

int main() {
	change(7);
}
void change(int& a) {...} // this doesn’t work

Move implementation

Move Constructor (rvalue&& reference)

Brutally rob other’s resources, because it is temporary and will disappear when the function exits.

Overloading between & and && versions of the same function allows us to disambiguate between copy and move.

Example:

vector<T>(vector<T>&& other):    // Move Constructor
	_size(other._size),
	_capacity(other._capacity) {
	// steal the other array 🕵
	_elems = other._elems;   // an l-value, performs a copy, to fix this, will introduce std::move
	other._elems = nullptr;
	other._size = 0;
}

Forcing a move to occur

Move Constructor:

vector<T>(vector<T>&& other):
	_size(std::move(other._size)),
	_capacity(std::move(other._capacity)) {
	// steal the other array 🕵
	_elems = std::move(other._elems); // std::move is a cast to a rvalue&&(equivalent to std::static_cast<T&&>)
	other._elems = nullptr;
	other._size = 0;
}

vector<T>& v = {1, 2, 3};
vector<T>& v2;
v2 = std::move(v); // std::move is a cast to a rvalue && (equivalent to std::static_cast<T&&>)

Takeaways

  • Use a constructor taking a rvalue for move constructor
  • Use operator= taking a rvalue for move assignment
  • Use std::move to make sure other object’s values are treated as rvalues (and so moved)
    • Call std::move to force anything to become a rvalue (and get its data taken!)
  • std::move does not move anything

swap and insert

Vector Example

template <typename T>
void vector<T>::push_back(const T& element) {
	elems[_size++] = element; // equals → copy
}

template <typename T>
void vector<T>::push_back(T&& element) {
	elems[_size++] = std::move(element); // move!
}

std:swap

template <typename T>
void swap(T& a, T& b) noexcept {
	T c(std::move(a)); // move constructor
	a = std::move(b); // move assignment
	b = std::move(c); // move assignment
} 

Haha, congrats, the 14th lecture finished! Seems CS106L is almost over, I will jump to another course CSE333 soon.

2020_11_27_3

References