4つのauto、4つのC++規格

プログラミング言語C++のキーワード `auto` による型推論のおかげで、C++プログラマはソースコード上で型(type)を明示しないプログラムを書けるようになりました。

2020年にリリース予定されている最新仕様「C++20」では、とうとう下記コードのように`auto`まみれの関数テンプレート[^purpose]を書けるようになります。ナニコレ...

```c++:cpp20.cpp
template <auto N>
auto func(auto x)
{
  auto r = N + x;
  return r;
}

// 呼出し例
assert( func<1>(0.5) == 1.5 );
```

この記事では、C++言語仕様改定により導入された各`auto`キーワードの役割を説明します。同時に、該当用法が存在しなかった時代でもコンパイル可能なソースコードへの書換えも行います。

[^purpose]: 各`auto`キーワードの役割説明のためのコードですから、関数テンプレートの具体的な処理内容にはあまり意味がありません。


## C++20時代

C++20では **関数パラメータ型に`auto`を書ける** ようになります。これはC++言語仕様へのコンセプト(Concept)導入とともに追加された、[関数テンプレート定義の短縮記法](https://yohhoy.hatenadiary.jp/entry/20190111/p1) です。

```c++:cpp20.cpp(再掲)
template <auto N>
auto func(/*👉*/auto/*👈*/ x)
{
  auto r = N + x;
  return r;
}
```

現行C++17仕様の範囲内で書き直す場合、関数テンプレートに新たに型パラメータ`U`を導入し、引数型として利用すればOKです。

```c++:cpp17.cpp
template <auto N, typename U>
auto func(U x)
{
  auto r = N + x;
  return r;
}
```

これならもう読めますよね？（無理？）


## C++17時代

C++17では **テンプレートパラメータ型に`auto`を書ける** ようになりました。つまり [非型テンプレートパラメータのauto宣言](https://cpprefjp.github.io/lang/cpp17/declaring_non-type_template_arguments_with_auto.html) です。

```c++:cpp17.cpp(再掲)
template </*👉*/auto/*👈*/ N, typename U>
auto func(U x)
{
  auto r = N + x;
  return r;
}
```

C++14仕様で書き直す場合、関数テンプレートに新たな型パラメータ`T`を導入し、そのうえで非型テンプレート引数`N`を取らねばなりません。書き直した関数テンプレートには`T`と`N`の両パラメータを指定する必要があるため、呼出し側コードの記述も冗長になります。

```c++:cpp14.cpp
template <typename T, T N, typename U>
auto func(U x)
{
  auto r = N + x;
  return r;
}

// 呼出し例
assert( func<decltype(1), 1>(0.5) == 1.5 );
```

冗長。ダサい。しかし気を落とさずに時代逆行を続けましょう。


## C++14時代

C++14では **関数戻り値型に`auto`とだけ書いておける** ようになりました。関数本体にあるreturn文の内容から戻り値型が推論される、[通常関数の戻り値型推論](https://cpprefjp.github.io/lang/cpp14/return_type_deduction_for_normal_functions.html) という機能です。

```c++:cpp14.cpp(再掲)
template <typename T, T N, typename U>
/*👉*/auto/*👈*/ func(U x)
{
  auto r = N + x;
  return r;
}
```

C++11仕様で書き直す場合、戻り値型の`auto`キーワードはそのまま使えますが、関数宣言末尾への型明示が求められます。つまり関数本体のreturn文で返す予定の型を、宣言時にも型導出しておく必要があるのです。

```c++:cpp11.cpp
template <typename T, T N, typename U>
auto func(U x) -> decltype(N + x)
{
  auto r = N + x;
  return r;
}

// 呼出し例
assert( func<decltype(1), 1>(0.5) == 1.5 );
```

なにこれ面倒くさい。[もうちょっとだけ続くんじゃ](https://dic.pixiv.net/a/%E3%82%82%E3%81%86%E3%81%A1%E3%82%87%E3%81%A3%E3%81%A8%E3%81%A0%E3%81%91%E7%B6%9A%E3%81%8F%E3%82%93%E3%81%98%E3%82%83)


## C++11時代

C++11は **キーワード`auto`が型推論の意味を持つ** ようになった最初のバージョンです。厳密には [変数の型推論](https://cpprefjp.github.io/lang/cpp11/auto.html) と [戻り値の型を後置する関数宣言構文](https://cpprefjp.github.io/lang/cpp11/trailing_return_types.html) の`auto`キーワードは異なる意味を持ちますが、さほど気にしなくても良いでしょう（前者＝型推論／後者＝プレースホルダ）。

```c++:cpp11.cpp(再掲)
template <typename T, T N, typename U>
auto func(U x) -> decltype(N + x)
{
  /*👉*/auto/*👈*/ r = N + x;
  return r;
}

// 呼出し例
assert( func<decltype(1), 1>(0.5) == 1.5 );
```

さて、これをC++03仕様で書き直すのは本当に大仕事です。C++03時代の`auto`キーワードは実効上の意味をもたず[^cpp03auto]、[式から型を導出する`decltype`](https://cpprefjp.github.io/lang/cpp11/decltype.html) も存在しません ＼(^o^)／

[^cpp03auto]: C++03時代のキーワード`auto`は、ローカル変数が「自動記憶域期間を持つ」という意味でした。つまり何気なく記述するローカル変数宣言 `int a;` は `auto int a;` と等しいという意味です。当然ながらこの目的で`auto`を明示記述するC++プログラマなど存在せず、C++11からは新たに「型推論」の意味を与えられて復活しました。


## C++03暗黒時代

暗黒のC++03時代には、偉大なる [Boostライブラリ](https://www.boost.org/) が大抵の問題を解決してくれました（ほんまか？）。2019年にもなって古のテクニックを紹介する意味もないでしょうから、意図通り動いたソースコードだけ貼っておひらきとします。

```cpp03_with_boost.cpp
#include <boost/typeof/typeof.hpp>
#include <boost/utility/declval.hpp>

template <typename T, typename U>
struct result_of_add {
  typedef BOOST_TYPEOF_TPL(
    boost::declval<T>() + boost::declval<U>()
  ) type;
};

template <typename T, T N, typename U>
typename result_of_add<T, U>::type
func(U x) {
  BOOST_AUTO(r, N + x);
  return r;
}

// 呼出し例
assert(( func<BOOST_TYPEOF(1), 1>(0.5) == 1.5 ));
```

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">なんで2019年にもなってBoost library on C++03を調べてしまったんだろうという虚無感を味わった</p>&mdash; yoh (@yohhoy) <a href="https://twitter.com/yohhoy/status/1109320786332905472?ref_src=twsrc%5Etfw">March 23, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>