2020年5月7日木曜日

C++ Templates - The Complete Guide, 2nd 読書メモ 1章 関数テンプレート

C++ Templates - The Complete Guide, 2ndの1章「関数テンプレート」の読書メモ

関数テンプレートとは

int max(int, int);
double max(double, double);
string max(string, string);

このように,型が違うだけで同じような関数をまとめて,次のように型名をtypenameで仮引数として定義できる.

template <typename T>
T max(T, T);

family of functions,という表現がポイントなのかな,と.

この関数テンプレートは,次のように呼び出せる.

max(1, 3);
max(1.0, 3.0);

このとき,具体的にTにintやdoulbeがあてはめられて関数が生成される.具体的な関数が生成されることをインスタンス化(instantiation)と呼び,生成された関数をテンプレートのインスタンス(instance)と呼ぶ.

Two-Phase Translation

テンプレートは,定義をしたタイミングと,具体的な型が割り当てられたタイミングの2つのタイミングで解釈が行われる.例えば,上記のmaxであれば大小比較が必要なので,大小比較が定義されていない型が渡されると,インスタンス化のタイミングでエラーとなる.

一方で,テンプレートの定義時点で定義されていない型名が関数テンプレート内で利用されている,といった文法上のエラーなどがある場合に,仕様上はエラー判定が行われるのだけれど,Microsoft Visual C++はその辺が緩い.Clangだとちゃんとエラーになる.その辺の挙動を切り替える方法については,次のリンク先にまとめてある.


型推論時の型変換

関数テンプレートは,呼び出し時に渡された実引数から型を自動的に決定する.これを型推論(Type Deduction)と言う.

この型推論が行われる際には,型変換が制限される.例えば,doubleを受け取る関数にintの値を渡しても,普段であれば暗黙の型変換が行われるが,関数テンプレートの場合,エラーになる.
max(1, 3.0); // コンパイルエラー

これを回避するには,明示的にキャストをするか,関数テンプレートの型を直接していする.

max(1, 3.0); // T=doubleとして判定される.

ちなみに,必要性が分からないのだけれど,デフォルト実引数を指定していたとしても,デフォルト実引数の型をもとに型推論は行われない,とのこと.

複数のテンプレート仮引数


標準ライブラリのmaxはそうなっていないけれど,説明のためにmaxのテンプレート仮引数を複数にして,複数の型を受け取れるようにしよう,という説明が進む.

こうすると,今度は返り値の型はどちらにそろえるんだ,といった問題が発生する.

そこで,返り値の型を推論する方法が示される.(書籍よりコードを抜粋)

template <typename T1, typename T2>
auto max(T1 a, T2 b) -> std::decay_t<decltype(true ? a : b)>
{
    return b < a ? a : b;
}

返り値の型を指定するところにautoを書いて,後ろにアロー(->)を書いて,decltypeで式の結果の型を求める.結果が参照などでは困る場合があるので,std::decay_tを利用して返り値の型に適切な型に変換する.

次のように,std::common_type_tを利用する方法も示されている.

template <typename T1, typename T2>
std::common_type_t max(T1 a, T2 b)
{
    return b < a ? a : b;
}

あくまでも説明のために出しているだけで,実際にこれをやると,intとdouble,doubleとint,のように型がひっくり返っているだけでも別の関数が生成されてしまい,プログラムサイズが無駄に増えるので望ましくないと思われる.

ちなみに,a > bとしないのは,C++の仕様を見るに,基本的には < を基準に比較演算が考えられているからだと思われる.

デフォルトテンプレート実引数


通常の関数と同様に,テンプレート仮引数にもデフォルト実引数を指定することができる.ただし,関数テンプレートにデフォルトテンプレート実引数を指定できるようになったのは,C++11以降なので注意が……今時必要ない気がするけど,未だにC++11さえも使えない環境もあるかもしれない.そうだとすると相当ツライ……

関数テンプレートのオーバーロード


通常の関数と同様に,関数テンプレートもオーバーロードできる.これ,意外と驚かれる.通常の関数もオーバーロードに含めた場合,どちらが優先されるかなどについては,13章で詳しく扱うようだ.第一版では9章だったので,4章ほど追加されているのかな.

その他


この単純なmax関数だけでも,色々考えることがあって,値渡しと参照渡し,どちらが良いのか,inlineにはできないのか,constexprは,など色々考えることがあるよ,と.

0 件のコメント:

コメントを投稿