LoginSignup
185
103

More than 5 years have passed since last update.

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

Last updated at Posted at 2019-03-23

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

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

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

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

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

C++20時代

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

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

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

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

これならもう読めますよね?(無理?)

C++17時代

C++17では テンプレートパラメータ型にautoを書ける ようになりました。つまり 非型テンプレートパラメータのauto宣言 です。

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

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

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文の内容から戻り値型が推論される、通常関数の戻り値型推論 という機能です。

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

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

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++11時代

C++11は キーワードautoが型推論の意味を持つ ようになった最初のバージョンです。厳密には 変数の型推論戻り値の型を後置する関数宣言構文autoキーワードは異なる意味を持ちますが、さほど気にしなくても良いでしょう(前者=型推論/後者=プレースホルダ)。

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キーワードは実効上の意味をもたず2式から型を導出するdecltype も存在しません \(^o^)/

C++03暗黒時代

暗黒のC++03時代には、偉大なる Boostライブラリ が大抵の問題を解決してくれました(ほんまか?)。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 ));


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

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

185
103
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
185
103