autoの推論まとめ、あるいはautoは忖度などしない件


# はじめに

auto、便利ですね。多めに使うだけでモダンなC++っぽくなり、俺TUEEEE感が出ます。
ただ、C++03時代には型にconstやら参照をそれなりに気を遣って付けていたので、autoを使うと一抹の不安がよぎります。

**うわっ･･･私のauto、copyしすぎ･･･？**

`auto`だけで良かったっけ？ 参照とか、constとか、どうすんだっけって悩むのは私だけでしょうか...

基本的には先人が驚きが少なくなる仕様としてくれているはずなのですが、そこはC++、どんな落とし穴があるのか分からないため、ざらっと確認してみます。

# 仕様の参照元

auto型推論の情報源としては、[Effective Modern C++](https://www.oreilly.co.jp/books/9784873117362/)および[光成さんの勉強会資料](https://www.slideshare.net/herumi/template-44013078)を参照しました。
ここでは仕様の詳細には踏み込まず、まとめサイトレベルの結論だけまとめてしまおうと思います。


# テンプレート型推論

autoの型推論は、テンプレートの型推論と同じになっています。なので、まずはテンプレートの型推論結果をみてみましょう。

## 説明のためのお約束

```cpp
// こんなテンプレートがあって、
template<typename T> void func(ParamType param);
// こう呼んだとします
func(expr);
```
ここで`ParamType`は説明用のメタな文字です。実際にはたとえば `T` とか、装飾された`const T&`とかがはいります。

たとえばこんなふう:

```cpp
template<typename T> void func(const T& param);

func(expr);
```

`expr`もまた、説明用のメタな文字です。実際には、`42`とか変数`x`とかが入ります。
たとえばこんなふう:

```cpp
template<typename T> void func(const T& param);

int x = 10;
func(x);
```

以上のメタな文字`ParamType` と `expr`をいろいろ変えてみて、`T`と`ParamType`が結局何になるのかの一覧表を示します。

右辺値は目立つよう<font color="Blue">(int)rvalue</font> としています。また、ちょっと注意が必要な欄は赤にしています。

ついでに`ParamType`欄に、つけた装飾が持つ意味を一言書いてみました。



<table>
<tr><th>ParamType         <th> expr                 <th>T           <th> deduced ParamType
<tr><td rowspan=7>T param <br>「全部コピーして所有する」<td> (int)lvalue <td> int <td> int param
<tr>                      <td> (const int)lvalue    <td> int        <td> int param
<tr>                      <td> (int&)lvalue         <td> int        <td> int param
<tr>                      <td> (const int&)lvalue   <td> int        <td> int param
<tr>                      <td> (int*)lvalue         <td> int*       <td> int* param
<tr>                      <td> (const int*)lvalue   <td> const int* <td> const int* param
<tr>                      <td> <font color="Blue">(int)rvalue          <td> int        <td> <font color="Red">int<br> ※コピーコンストラクタが呼ばれるかどうかは時と場合による

<tr><td rowspan=7>T* param <br>「ポインタしか受け取らない」<td> (int)lvalue <td colspan=2> <font color="SlateGray">ポインタでないのでコンパイルエラー
<tr>                      <td> (const int)lvalue    <td colspan=2> <font color="SlateGray">ポインタでないのでコンパイルエラー
<tr>                      <td> (int)lvalue          <td colspan=2> <font color="SlateGray">ポインタでないのでコンパイルエラー
<tr>                      <td> (const int&)lvalue   <td colspan=2> <font color="SlateGray">ポインタでないのでコンパイルエラー
<tr>                      <td> (int*)lvalue         <td> int        <td> int* param
<tr>                      <td> (const int*)lvalue   <td> const int  <td> const int* param
<tr>                      <td> <font color="Blue">(int)rvalue          <td colspan=2> <font color="SlateGray">ポインタでないのでコンパイルエラー

<tr><td rowspan=7>const T* param <br>「ポインタしか受け取らない。受け取ったポインタが指す先は変更しない。」<td> (int)lvalue <td colspan=2><font color="SlateGray"> ポインタでないのでコンパイルエラー
<tr>                      <td> (const int)lvalue    <td colspan=2><font color="SlateGray"> ポインタでないのでコンパイルエラー
<tr>                      <td> (int)lvalue          <td colspan=2><font color="SlateGray"> ポインタでないのでコンパイルエラー
<tr>                      <td> (const int&)lvalue   <td colspan=2><font color="SlateGray"> ポインタでないのでコンパイルエラー
<tr>                      <td> (int*)lvalue         <td> int        <td> const int* param
<tr>                      <td> (const int*)lvalue   <td> int        <td> const int* param
<tr>                      <td><font color="Blue">(int)rvalue          <td colspan=2><font color="SlateGray"> ポインタでないのでコンパイルエラー

<tr><td rowspan=7>T& param <br>「コピーしない。あるがまま参照する。受け取ったパラメータは変更するかも。」<td> (int)lvalue <td> int <td> int& param
<tr>                      <td> (const int)lvalue    <td> const int  <td> const int& param
<tr>                      <td> (int&)lvalue         <td> int        <td> int& param
<tr>                      <td> (const int&)lvalue   <td> const int  <td> const int& param
<tr>                      <td> (int*)lvalue         <td> int*       <td> int*& param
<tr>                      <td> (const int*)lvalue   <td> const int* <td> const int*& param
<tr>                      <td> <font color="Blue">(int)rvalue          <td colspan=2><font color="SlateGray"> 左辺値参照に一時オブジェクトは渡せないのでコンパイルエラー

<tr><td rowspan=7>const T& param <br>「コピーしない。const参照する。参照先は変更しない、という意思。」<td> (int)lvalue <td> int <td> const int& param
<tr>                      <td> (const int)lvalue    <td> int        <td> const int& param
<tr>                      <td> (int&)lvalue         <td> int        <td> const int& param
<tr>                      <td> (const int&)lvalue   <td> int  <td> const const int& param
<tr>                      <td> (int*)lvalue         <td> int*       <td> int* const& param
<tr>                      <td> (const int*)lvalue   <td> const int* <td> const int* const& param
<tr>                      <td> <font color="Blue">(int)rvalue          <td> int        <td> <font color="Red">const int& param ※const参照が一時オブジェクトをバインドする奴

<tr><td rowspan=7>T&& param <br>「コピーしない。あるがまま参照する。受け取ったパラメータは変更するかも。一時オブジェクトは捕まえる。」<td> (int)lvalue <td> int& <td> int& param
<tr>                      <td> (const int)lvalue    <td> const int&  <td> const int& param
<tr>                      <td> (int&)lvalue         <td> int&        <td> int& param
<tr>                      <td> (const int&)lvalue   <td> const int&  <td> const int& param
<tr>                      <td> (int*)lvalue         <td> int*&       <td> int*& param
<tr>                      <td> (const int*)lvalue   <td> const int*& <td> const int*& param
<tr>                      <td> <font color="Blue">(int)rvalue          <td> const int    <td> <font color="Red">int&& param ※右辺値参照を捕まえるやつ。move。</font>

</table>

`T&&` - [ユニバーサル参照あるいはフォワード参照](https://stackoverflow.com/questions/39552272/is-there-a-difference-between-universal-references-and-forwarding-references) - はそれ単体で別途説明が必要なモノではありますが、全体としては納得の推論です。
いままでコンパイラに怒られなければいっかとフィーリングで実装していましたが、問題なさそうで安心しました。

# auto型推論

テンプレート型推論の `T`を `auto`に、`ParamType`を`auto`の修飾に変更すれば、`auto`の型推論表のできあがりです。

<table>
<tr><th>ParamType         <th> expr                  <th> auto推論結果
<tr><td rowspan=7>auto param <br>「全部コピーして所有する」<td> = (int)lvalue <td> int param
<tr>                      <td> = (const int)lvalue    <td> int param
<tr>                      <td> = (int&)lvalue         <td> int param
<tr>                      <td> = (const int&)lvalue   <td> int param
<tr>                      <td> = (int*)lvalue         <td> int* param
<tr>                      <td> = (const int*)lvalue   <td> const int* param
<tr>                      <td> <font color="Blue">= (int)rvalue          <td>  <font color="Red">int<br> ※コピーコンストラクタが呼ばれるかどうかは時と場合による

<tr><td rowspan=7>auto* param <br>「ポインタしか受け取らない」<td> (int)lvalue <td> <font color="SlateGray">ポインタでないのでコンパイルエラー
<tr>                      <td> = (const int)lvalue    <td> <font color="SlateGray">ポインタでないのでコンパイルエラー
<tr>                      <td> = (int)lvalue          <td> <font color="SlateGray">ポインタでないのでコンパイルエラー
<tr>                      <td> = (const int&)lvalue   <td> <font color="SlateGray">ポインタでないのでコンパイルエラー
<tr>                      <td> = (int*)lvalue         <td> int* param
<tr>                      <td> = (const int*)lvalue   <td> const int* param
<tr>                      <td> <font color="Blue">= (int)rvalue          <td><font color="SlateGray">ポインタでないのでコンパイルエラー

<tr><td rowspan=7>const auto* param <br>「ポインタしか受け取らない。受け取ったポインタが指す先は変更しない。」<td> = (int)lvalue <td><font color="SlateGray"> ポインタでないのでコンパイルエラー
<tr>                      <td> = (const int)lvalue    <td><font color="SlateGray"> ポインタでないのでコンパイルエラー
<tr>                      <td> = (int)lvalue          <td><font color="SlateGray"> ポインタでないのでコンパイルエラー
<tr>                      <td> = (const int&)lvalue   <td><font color="SlateGray"> ポインタでないのでコンパイルエラー
<tr>                      <td> = (int*)lvalue         <td> const int* param
<tr>                      <td> = (const int*)lvalue   <td> const int* param
<tr>                      <td><font color="Blue">= (int)rvalue          <td><font color="SlateGray"> ポインタでないのでコンパイルエラー

<tr><td rowspan=7>auto& param <br>「コピーしない。あるがまま参照する。受け取ったパラメータは変更するかも。」<td> = (int)lvalue <td> int& param
<tr>                      <td> = (const int)lvalue    <td> const int& param
<tr>                      <td> = (int&)lvalue         <td> int& param
<tr>                      <td> = (const int&)lvalue   <td> const int& param
<tr>                      <td> = (int*)lvalue         <td> int*& param
<tr>                      <td> = (const int*)lvalue   <td> const int*& param
<tr>                      <td> <font color="Blue">= (int)rvalue          <td><font color="SlateGray"> 左辺値参照に一時オブジェクトは渡せないのでコンパイルエラー

<tr><td rowspan=7>const auto& param <br>「コピーしない。const参照する。参照先は変更しない、という意思。」<td> = (int)lvalue <td> const int& param
<tr>                      <td> = (const int)lvalue    <td> const int& param
<tr>                      <td> = (int&)lvalue         <td> const int& param
<tr>                      <td> = (const int&)lvalue   <td> const const int& param
<tr>                      <td> = (int*)lvalue         <td> int* const& param
<tr>                      <td> = (const int*)lvalue   <td> const int* const& param
<tr>                      <td> <font color="Blue">= (int)rvalue          <td> <font color="Red">const int& param ※const参照が一時オブジェクトをバインドする奴

<tr><td rowspan=7>auto&& param <br>「コピーしない。あるがまま参照する。受け取ったパラメータは変更するかも。一時オブジェクトは捕まえる。」<td> = (int)lvalue <td> int& param
<tr>                      <td> = (const int)lvalue    <td> const int& param
<tr>                      <td> = (int&)lvalue         <td> int& param
<tr>                      <td> = (const int&)lvalue   <td> const int& param
<tr>                      <td> = (int*)lvalue         <td> int*& param
<tr>                      <td> = (const int*)lvalue   <td> const int*& param
<tr>                      <td> <font color="Blue">= (int)rvalue          <td> <font color="Red">int&& param ※右辺値参照を捕まえるやつ。move。</font>
</table>

# おわりに

一通りまとめましたが、赤文字部分の詳細とか、配列を実引数に入れた場合とか、関数ポインタを入れた場合とか、波括弧 (初期化の統一記法) での初期化など、語るべき点が残っています。また、「仕様はわかったから実際のユースケースくれ」というむきもあるでしょう。
そのあたりついては、おいおい追記したいと思っていますが、勉強しながらになりますのでしばしお待ちください。

本記事に興味がある方・もっとkwskという方は、Effective Modern C++を参照頂ければと思います。というか、そこに全部書いてあります。