それってコピーなのかムーブなのか

## ことの発端

速度効率やメモリ効率を考えながらコーディングを進めると、コピーなのかムーブなのかは、パフォーマンスに大きく影響されるところです。
ここで登場してくるのが `ムーブセマンティクス（move semantics）` や `右辺値参照（rvalue refefence）` なのですが、これがまた難しい。言っていることは単純なんだけど、C++ の仕様上、これがコピーなのかムーブなのかが分かりづらく[^1]、色んな言語でコード書いていると、しょっちゅう混乱するので、具体的にコードを書いて、一年後の自分のためにメモしようと思った次第です。 

[^1]: この辺、`Rust` が良くできているな〜と思います。当初は代入で右辺の所有権が左辺に奪われるというセンセーショナルな言語仕様でしたが、効率化を考えながらコードを書いている人間には素晴らしいものなんだと思います。


## 登場人物

### `FOO` クラス

`FOO` クラスは `bHasValue` というメンバ変数を持っています。
この `bHasValue` はコンストラクタで常に `true` に設定されます。
値をムーブする際に、 `bHasValue` は `false` に設定されます。（所有権を奪われたイメージ）

実際にはこんなクラスは何の役にも立たないかもすが、ムーブセマンティクスや右辺値参照を確認するにはもってこいなんじゃないかと思います。

```cpp
struct FOO {
  bool bHasValue;

  FOO() : bHasValue(true)
    { std::cout << "default ctor" << std::endl; }
    
  FOO(const FOO& src) : bHasValue(true) 
    { std::cout << "copy ctor" << std::endl; } 

  FOO(FOO&& src) : bHasValue(true)
    { std::cout << "move ctor" << std::endl; src.bHasValue = false; }

  void operator = (const FOO& src)
    { std::cout << "copy =" << std::endl; }

  void operator = (FOO&& src)
    { std::cout << "move =" << std::endl; src.bHasValue = false; }

  ~FOO() { std::cout << "dtor " << bHasValue << std::endl; }    
};
```

ちなみに `operator =()` は今回登場しません。（次回はあるのか？）

### `bar()` 関数

`bar()` 関数は `FOO` のインスタンスを生成して返却します。

```cpp
FOO bar() { return FOO(); }
```

### `baz()` 関数

`baz()` 関数は `FOO` の const 参照を返却します。
クラスとかで、オブジェクトプロパティを取得する時なんかで良く使う感じを模試します。

```cpp
static const FOO static_foo;
const FOO& baz() { return static_foo; }
```

### `hoge()` 関数

`hoge()` は与えられた引数によってコピーかムーブかが決まります。
つまり、どちらが呼ばれるかはメモリの効率に大きく影響が出るところです。

```cpp
void hoge(const FOO&)
{
  /* コピー用 */
  std::cout << "hoge const &" << std::endl;
}
void hoge(FOO&&)
{
  /* ムーブ用 */
  std::cout << "hoge &&" << std::endl;
}
```


## 何が起こるか

さて、材料が揃いましたので、これから、 `&` や `&&` や `std::move()` なんかを使うと何が起こるか観察します。
なお、 `static const FOO static_foo;` も `main()` 関数の前後でログが出力されるのですが、ノイズになるので敢えて結果で表示していません。

### その前に `右辺値` と `右辺値参照` と `&` と `&&` のおさらい

`&` も `&&` も何れも `参照` なので、`auto& f = xx;` やら `auto&& f = xx;` やらと書いたとしても どちらの変数 `f` も `右辺値` でもなければ `左辺値` でもありません。よもや代入式でありながら代入なんて行われていません。あくまで、右辺（の結果）を `参照` しているだけです。 `キャプチャ` とか `エイリアス` とかって言いますね。
対して、引数で登場する `&&` 、例えば `hoge(FOO&&)` みたいなのは「右辺値を参照するのだから右辺値をよこせ」と言ってます。なので `hoge` を呼び出す際に必要な引数は `右辺値` でなければなりません。 `右辺値の参照` は右辺値ではありません。（何言ってるんだという場合はパターン１４へ）

`右辺値` とはある一瞬に現れる値だったりインスタンスだったりを指します[^2]。 つまり `右辺値` として存在する期間は「刹那」です。「刹那」故にせっかく生成されたインスタンスであ〜だこ〜だすることがやり難いものでした。しかし、それは勿体ないと C++11 で、その `右辺値` をキャプチャできるヤツが現れました。これが `右辺値参照` です。ここまで良いでしょう。しかし、　`右辺値参照` はその「刹那」なものとはかけ離れ、一定期間インスタンスが存在することとなります。つまり、 <b> `右辺値` を参照（キャプた）するための `右辺値参照` ですが、`右辺値参照` そのものは `右辺値` ではありせん。</b> 個人的にはここが理解の壁だったので、あえて太字で書いておきます。

[^2]: [右辺値とは名前をもたない一時的なオブジェクトである](https://cpprefjp.github.io/lang/cpp11/rvalue_ref_and_move_semantics.html)

### パターン１

```cpp:パターン１
int main() {
  FOO foo;
  auto& f = foo;
  auto&& ff = foo;
}
```
```:結果（パターン１）
default ctor
dtor 1
```

基本中の基本なので、特に問題無いかな？
`auto&` と `auto&&` の違いは後で出てきます。


### パターン２

```cpp:パターン２
int main() {
  FOO foo;
  auto f = foo;
}
```
```:結果（パターン２）
default ctor
copy ctor
dtor 1
dtor 1
```

これも基本中の基本ですね。
ちゃんとコピーコンストラクタが呼び出されています。

### パターン３

```cpp:パターン３
int main() {
  FOO foo;
  auto f = std::move(foo);
}
```
```:結果（パターン３）
default ctor
move ctor
dtor 1
dtor 0
```

例えば、`f` が登場した時点で `foo` が必要なくなった場合なんかは、```std::move()``` を使って所有権を移転します。
とはいえ、通常のプログラムでこのようなコードを書くことはまず無いと思いますが、ムーブセマンティクスの挙動を知る上では重要になります。

### パターン４

```cpp:パターン４
int main() {
  auto f = FOO();
}
```
```:結果（パターン４）
default ctor
dtor 1
```

さてさて、この辺から怪しい雰囲気になりますかね。（苦笑）
そうなんです。
`f` に代入していますが、コピーもムーブもされません。


### パターン５

```cpp:パターン５
int main() {
  auto& f = FOO();
}
```
```:結果（パターン５）
下記のコンパイルエラーが発生します。
non-const lvalue reference to type 'FOO' cannot bind to a temporary of type 'FOO'
```

パターン４で「`f` へのコピーが発生する」と思って、こんな書き方すると怒られます。

### パターン６

```cpp:パターン６
int main() {
  const auto& f = FOO();
  std::cout << "..." << std::endl;
}
```
```:結果（パターン６）
default ctor
...
dtor 1
```

ということでエラーに即して、 `const auto&` にすれば良いんじゃね？と書いて見ますが、結果はパターン３と同じですね。（`FOO`オブジェクトに対する`const` 属性が付加されますが、今回の主題はコピーなのかムーブなのかなので）
ちなみに `...` を表示してスコープ内で `f` が有効なのかを念の為確認しています。（一時オブジェクトってどこまでが一時なのかって、夢に出ることありません？私はありました。）


### パターン７

```cpp:パターン７
int main() {
  auto&& f = FOO();
  std::cout << "..." << std::endl;
}
```
```:結果（パターン７）
default ctor
...
dtor 1
```

`auto&&` としても、結果的にはパターン３と同じです。


### パターン８

```cpp:パターン８
int main() {
  auto f = bar();
}
```
```:結果（パターン８）
default ctor
dtor 1
```

`bar()` は `FOO` を生成して返却しますが、この場合、`FOO`の生成・破棄は１回のみです。[^3]
結構、みなさん勘違いしている方もいらっしゃるのでは？（私も勘違いしてました）

[^3]: `RVO`　(Return Value Optimization)　と　`NRVO`　(Named RVO)　によるもの。`RVO`は一時オブジェクトを返却する場合で、`NRVO`は関数内で変数定義（オブジェクトインスタンス）されたものを返却する場合、呼び出し元でのコピーを省略する（できる）場合があるとする仕様。[コピー省略](https://ja.cppreference.com/w/cpp/language/copy_elision)と[値のコピー省略を保証(C++17)](https://cpprefjp.github.io/lang/cpp17/guaranteed_copy_elision.html)を参照。

### パターン９

```cpp:パターン９
int main() {
  FOO f = bar();
}
```
```:結果（パターン９）
default ctor
dtor 1
```

パターン８は 「`auto` だからか？」と疑っている方のために、念の為`FOO`で受けてみるも結果は同じです。


### パターン１０

```cpp:パターン１０
int main() {
  FOO f = baz();
}
```
```:結果（パターン１０）
copy ctor
dtor 1
```

`baz()` は `const FOO&` を返却します。
ここでパターン９の `FOO` を返却する `bar()` 比較しましょう。


### パターン１１

```cpp:パターン１１
int main() {
  auto f = baz();
}
```
```:結果（パターン１１）
copy ctor
dtor 1
```

パターン１０を `auto` で受けても同様にコピーが発生します。


### パターン１２

```cpp:パターンX
int main() {
  auto& f = baz();
}
```
```:結果（パターンX）
```

コピーもムーブもされません。
単に `baz()` の返却値をキャプチャします。


### パターン１３

```cpp:パターン１３
int main() {
  auto&& f = baz();
}
```
```:結果（パターン１３）
```

こちらも、コピーもムーブもされません。
単に `baz()` の返却値をキャプチャします。

### パターン１４

```cpp:パターン１４
void fuga(FOO&& f) { FOO ff = f; }
void piyo(FOO&& f) { FOO ff = std::move(f); }

int main() {
  std::cout << "fuga() begin" << std::endl;
  fuga(FOO());
  std::cout << "fuga() end" << std::endl;

  std::cout << "piyo() begin" << std::endl;
  piyo(FOO());
  std::cout << "piyo() end" << std::endl;
}
```
```:結果（パターン１４）
default ctor
copy ctor
dtor 1
dtor 1
fuga() end
piyo() begin
default ctor
move ctor
dtor 1
dtor 0
piyo() end
```

たま〜に `fuga(FOO&& f);` で登場する `f` って右辺値なんじゃないかと勘違いするのですが、違います。
`右辺値を参照`しているので`右辺値`ではないです。`右辺値` としてではなく `左辺値` [^4] として取り扱われてコピーが発生します。

[^4]: これを `左辺値` と言っていいのかな？　あくまで `右辺値の参照` であって `右辺値` ではないと解するべきなのか。。。

## 余談

で、パターン１４の流れで、完全転送という何ともカッコイイ `std::forward` に登場していただき `fuga(FOO&& f);` をこんな感じで書くとコピーは発生せずムーブしなくなり

```cpp
void fuga(FOO&& f) { FOO ff = std::forward<FOO&&>(f); }
```

更に進めると `FOO&&` を `T` に一般化して

```cpp:これはダメで
template <typename T> void fuga(T f) { FOO ff = std::forward<T>(f); }
```

脳内では `T` が `右辺値の参照` なら右辺値として、 `左辺値の参照` ならそのまま `左辺値の参照` として、`型` と `値` が転送されるから、これでコピーとムーブもバッチリじゃん、なんて思っていたんですよ。
しかし、現実には、そうは問屋が卸さず、

```cpp:こうしなければなりません
template <typename T> void fuga(T&& f) { FOO ff = std::forward<T>(f); }
```

こうする必要があります。
実は、別件でモヤモヤしている件があって、突き進めるとこれがオチになるという結果が待っていました。 [^5]

https://qiita.com/terukazu/items/d7c49aff5624e038b92b

[^5]: 著者以外は意味不明な感じの言い回しですが、ここで `template` の話を進めると、記事の本質から逸脱して、 C++ の整然とした混沌の世界に迷い込みそうなので、深い話はなしにして、記事のリンクを貼るだけにしました。