プログラミング言語C++のキーワード auto
による型推論のおかげで、C++プログラマはソースコード上で型(type)を明示しないプログラムを書けるようになりました。
2020年にリリース予定されている最新仕様「C++20」では、とうとう下記コードのようにauto
まみれの関数テンプレート1を書けるようになります。ナニコレ...
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)導入とともに追加された、関数テンプレート定義の短縮記法 です。
template <auto N>
auto func(/*👉*/auto/*👈*/ x)
{
auto r = N + x;
return r;
}
現行C++17仕様の範囲内で書き直す場合、関数テンプレートに新たに型パラメータU
を導入し、引数型として利用すればOKです。
template <auto N, typename U>
auto func(U x)
{
auto r = N + x;
return r;
}
これならもう読めますよね?(無理?)
C++17時代
C++17では テンプレートパラメータ型にauto
を書ける ようになりました。つまり 非型テンプレートパラメータのauto宣言 です。
template </*👉*/auto/*👈*/ N, typename U>
auto func(U x)
{
auto r = N + x;
return r;
}
C++14仕様で書き直す場合、関数テンプレートに新たな型パラメータT
を導入し、そのうえで非型テンプレート引数N
を取らねばなりません。書き直した関数テンプレートにはT
とN
の両パラメータを指定する必要があるため、呼出し側コードの記述も冗長になります。
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文の内容から戻り値型が推論される、通常関数の戻り値型推論 という機能です。
template <typename T, T N, typename U>
/*👉*/auto/*👈*/ func(U x)
{
auto r = N + x;
return r;
}
C++11仕様で書き直す場合、戻り値型のauto
キーワードはそのまま使えますが、関数宣言末尾への型明示が求められます。つまり関数本体のreturn文で返す予定の型を、宣言時にも型導出しておく必要があるのです。
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
キーワードは異なる意味を持ちますが、さほど気にしなくても良いでしょう(前者=型推論/後者=プレースホルダ)。
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年にもなって古のテクニックを紹介する意味もないでしょうから、意図通り動いたソースコードだけ貼っておひらきとします。
#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 ));
なんで2019年にもなってBoost library on C++03を調べてしまったんだろうという虚無感を味わった
— yoh (@yohhoy) March 23, 2019