# Way to C++: 2. Move

[上一篇](https://dev.mukyu.tw/way-to-cpp11-1-copy-constructor)

在只有 copy 行為的情況下，會有不少的效能問題，例如一個最常見的用法：

```cpp
std::vector<int> getPrimes(int n); // return p\_1, ..., p\_n primes
auto primes = getPrimes(100000); // copy??
```

在只有 copy 語意的情況下， 第二行會需要複製大量的陣列元素，可是這是不太需要的。

所以 c++11 引進了所謂 r-value reference 的概念。

# R-value

r-value 可以直接理解為「只可以擺在 assignment 右邊的值」，與之相對的則是 l-value 「可以擺在左邊的值」。

一些直接的例子：

```cpp
int b;  
int foo(int a) {  
  return a;  
}  
int& bar() {  
  return b;  
}

// b: l-value  
// foo(): r-value  
// bar(): l-value  
// (b+1): r-value  
// 99: r-value
```

而最上面的 `getPrimes(100000)` 也是 r-value。

而 r-value reference 就是 `type&&`

一個用 code 來分辨的方式可以這樣寫：

```cpp
void foo(const int& a) {  
  puts("l-value");  
}  
void foo(int&& a) {  
  puts("r-value");  
}  
int main() {  
  int a = 1;  
  foo(a); // l-value  
  foo(a + 1); // r-value  
  return 0;  
}
```

# 搬移

class 裡面有兩個搬移函數，分別是：

*   move constructor
*   move assignment

```cpp
C::C(C&& c);  
C& C::operator=(C&& c);
```

要自行實作搬移函數，除了把指標位置複製過進來，還要把被搬移的東西設置成「空」。

舉上一篇的 `IntBuff` 來說，我們可以這樣寫：

```cpp
IntBuff::IntBuff(IntBuff&& ib) {  
  buf = ib.buf;  
  ib.buf = nullptr;  
  sz = ib.sz;  
  ib.sz = 0;  
}

IntBuff& IntBuff::operator=(IntBuff&& ib) {  
  delete buf; // delete nullptr is ok  
  buf = ib.buf;  
  ib.buf = nullptr;  
  sz = ib.sz;  
  ib.sz = 0;  
}
```

因為在我們在「搬移」的同時，仍然注意原本被搬移的對象的指標，讓他的指標不會和自己的指標指向同一區塊，刪除時也不會出問題。

去看 [cppreference](https://en.cppreference.com/w/cpp/container/vector/vector) 可以發現， `std::vector` 確實有實作 `vector(vector&&)`，所以

```cpp
auto primes = getPrimes(100000);
```

確實可以觸發 move constructor，而不會觸發 copy constructor。

# `std::move`

一些可以直接是 r-value reference 的確實可以直接觸發搬移，可是假如希望一個變數觸發搬移呢？

```cpp
vector<int> a, b(10, 0);  
a = b; // copy
```

`std::move` 可以把傳進去的轉換成他的 r-value reference 型別。

```cpp
void foo(const int& a) {  
  puts("l-value");  
}  
void foo(int&& a) {  
  puts("r-value");  
}  
int main() {  
  int a = 1;  
  foo(a); // l-value  
  foo(std::move(a)); // r-value  
  return 0;  
}
```

# IntBuff version3

附上 IntBuff 有實作搬移的版本：

```cpp
#include <algorithm>
#include <cstdio>
#include <memory>

class IntBuff {
public:
    IntBuff() = default;
    explicit IntBuff(size_t sz) : sz(sz) {
        if (sz) {
            arr = new int[sz];
        }
    };

    IntBuff(const IntBuff& ib) {
        sz = ib.sz;
        arr = new int[sz];
        for (int lx = 0; lx < sz; lx++) {
            arr[lx] = ib.arr[lx];
        }
    }

    IntBuff& operator=(const IntBuff& ib) {
        IntBuff tmp(ib);  // copy-and-swap
        std::swap(tmp.arr, arr);
        std::swap(tmp.sz, sz);
        return *this;
    }

    IntBuff(IntBuff&& ib) {
        arr = ib.arr;
        ib.arr = nullptr;
        sz = ib.sz;
        ib.sz = 0;
    }

    IntBuff& operator=(IntBuff&& ib) {
        delete arr;
        arr = ib.arr;
        ib.arr = nullptr;
        sz = ib.sz;
        ib.sz = 0;
        return *this;
    }

    ~IntBuff() {
        printf("%p\n", arr);
        delete[] arr;
    }

private:
    int* arr = nullptr;
    size_t sz = 0;
};

int main() {
    {
        IntBuff a1(size_t(10)), b1;
        b1 = std::move(a1);
    }
    return 0;
}
```
