# CSE332S Object-Oriented Programming in C++ (Lecture 15)

## Move semantics introduction and motivation

Review: copy control consists of 5 distinct operations

- A `copy constructor` initializes an object by duplicating the const l-value that was passed to it by reference
- A `copy-assignment operator` (re)sets an object’s value by duplicating the const l-value passed to it by reference
- A `destructor` manages the destruction of an object
- A `move constructor` initializes an object by transferring the implementation from the r-value reference passed to it
- A `move-assignment operator` (re)sets an object’s value by transferring the implementation from the r-value reference passed to it

Today we'll focus on the last 2 operations and other features (introduced in C++11) like r-value references
I.e., features that support the new C++11 `move semantics`

### Motivation for move semantics

Copy construction and copy-assignment may be expensive due to time/memory for copying
It would be more efficient to simply "take" the implementation from the passed object, if that's ok
It's ok if the passed object won't be used afterward

- E.g., if it was passed by value and so is a temporary object
- E.g., if a special r-value reference says it's ok to take from (as long as object remains in a state that's safe to destruct)

Note that some objects require move semantics

- I.e., types that don't allow copy construction/assignment
- E.g., `unique_ptr`, `ifstream`, `thread`, etc.

New for C++11: r-value references and move function

- E.g., `int i; int &&rvri = std::move(i);`

### Synthesized move operations

Compiler will only synthesize a move operation if  

- Class does not declare any copy control operations, and
- Every non-static data member of the class can be moved

Members of built-in types can be moved

- E.g., by `std::move` etc.

User-defined types that have synthesized/defined version of the specific move operation can be moved
L-values are always copied, r-values can be moved

- If there is no move constructor, r-values only can be copied

Can ask for a move operation to be synthesized

- I.e., by using `= default`
- But if cannot move all members, synthesized as `= delete`

## Move constructor and assignment operator examples, more details on inheritance

### R-values, L-values, and Reference to Either

A variable is an l-value (has a location)

- E.g., `int i = 7;`

Can take a regular (l-value) reference to it

- E.g., `int & lvri = i;`

An expression is an r-value

- E.g., `i * 42`

Can only take an r-value reference to it (note syntax)

- E.g., `int && rvriexp = i * 42;`

Can only get r-value reference to l-value via move

- E.g., `int && rvri = std::move(i);`
- Promises that i won’t be used for anything afterward
- Also, must be safe to destroy i (could be stack/heap/global)

### Move Constructors

```cpp
// takes implementation from a
IntArray::IntArray(IntArray &&a)
  : size_(a.size_), 
    values_(a.values_) {

  // make a safe to destroy
  a.values_ = nullptr;
  a.size_ = 0;
}
```

Note r-value reference

- Says it's safe to take a's implementation from it
- Promises only subsequent operation will be destruction

Note constructor design

- A lot like shallow copy constructor's implementation
- Except, zeroes out state of `a`
- No sharing, current object owns the implementation
- Object `a` is now safe to destroy (but is not safe to do anything else with afterward)

### Move Assignment Operator

No allocation, so no exceptions to worry about

- Simply free existing implementation (delete `values_`)
- Then copy over size and pointer values from `a`
- Then zero out size and pointer in `a`

This leaves assignment complete, `a` safe to destroy

- Implementation is transferred from `a` to current object

```cpp
Array & Array::operator=(Array &&a) { // Note r-value reference
  if (&a != this) { // still test for self-assignment
    delete [] values_;   // safe to free first (if not self-assigning)
	    size_ = a. size_;    // take a’s size value
	    values_ = a.values_; // take a’s pointer value
       a.size_ = 0;         // zero out a’s size
       a.values_ = nullptr; // zero out a’s pointer (now safe to destroy)
	  }
	  return *this; 
	}
```

### Move Semantics and Inheritance

Base classes should declare/define move operations

- If it makes sense to do so at all
- Derived classes then can focus on moving their members
- E.g., calling `Base::operator=` from `Derived::operator=`

Containers further complicate these issues

- Containers hold their elements by value
- Risks slicing, other inheritance and copy control problems

So, put (smart) pointers, not objects, into containers

- Access is polymorphic if destructors, other methods virtual
- Smart pointers may help reduce need for copy control operations, or at least simplify cases where needed
