関数テンプレートは関数のファミリーです。この投稿では、関数テンプレートについて深く掘り下げたいと思います。
同じページに移動するための短いリマインダーです。
max
などの関数テンプレートをインスタンス化する場合 int
の場合 と
double
template <typename T> T max(T lhs,T rhs) { return (lhs > rhs)? lhs : rhs; } int main() { max(10, 5); max(10.5, 5.5); }
コンパイラは、int および
double
用に完全に特殊化された関数テンプレートを生成します : max<int>
および max<double>
.ジェネリック部分はどちらの場合も空です: template<>
C++ Insights のおかげで、ここに洞察があります。
さて、これで詳細に飛び込むことができます。関数テンプレートと非テンプレート関数 (要するに関数) がオーバーロードするとどうなりますか?
関数テンプレートと関数のオーバーロード
関数 max
を使用させてください もう一度。今回は float
用にインスタンス化します そして double,
関数 max
を提供します 取ることも 2 倍になります。
これが私の次の例です:
template <typename T> T max(T lhs,T rhs) { return (lhs > rhs)? lhs : rhs; } double max(double lhs, double rhs) { return (lhs > rhs)? lhs : rhs; } int main() { max(10.5f, 5.5f); // (1) max(10.5, 5.5); // (2) }
あなたは私の質問を推測するかもしれません。行 (1) と (2) では何が起こるでしょうか?いくつか質問がありますか?
- 行 (1):コンパイラは関数テンプレートまたは関数を選択し、
float
をプロモートしますか?double
へ . - 行 (2):関数と関数テンプレートのいずれかが理想的に適合します。これはあいまいで、コンパイラ エラーを引き起こす可能性があります。
質問への回答は非常に直感的で、C++ の一般的なルールに従います。コンパイラは最適な関数を選択します。
- 行 (1):関数は float から double への昇格を必要とするため、関数テンプレートの方が適しています。
- 行 (2):関数テンプレートと関数は理想的に適合しています。この場合、追加のルールが適用されます。両方が同等に適合する場合、コンパイラは関数を優先します。
以前と同様に、C++ Insights はこのプロセスを視覚化するのに役立ちます。
スクリーンショットはそれを明示的に示しています。関数テンプレート max
の使用のみ float
で (2 行目) 関数テンプレートのインスタンス化をトリガーします。
関数テンプレートの基本を通して、さらに旅を続けましょう。
最初の免責事項:この投稿では概念を無視します。
さまざまなテンプレート引数
関数テンプレート max
を使用させてください 異なる型を持つ 2 つの値を持つ。
template <typename T> T max(T lhs,T rhs) { return (lhs > rhs)? lhs : rhs; } int main() { max(10.5f, 5.5); }
C++ Insights で試してみましょう:
わお!ここで何が起きてるの?なぜ float
なのか double
に昇格しない ?正直なところ、コンパイラの考え方は異なります。その方法を説明させてください。
- 可能であれば、コンパイラは関数の引数からテンプレートの引数を推測します。この場合、可能です。
- コンパイラは、関数の引数ごとにこのテンプレート引数推定のプロセスを実行します。
10.5f
の場合 コンパイラはfloat
を推測しますT,
の場合 for5.5
コンパイラはdouble
を推測しますT
の場合 .- もちろん、T を
float
にすることはできません とdouble
同時に。このあいまいさのために、コンパイルは失敗しました。
2 番目の免責事項:テンプレート引数推定のプロセスを簡略化しました。関数テンプレートとクラス テンプレートのテンプレート引数推定については、今後の投稿で追加の投稿を書く予定です。
もちろん、異なる型の値を比較したい.
2 つの型パラメータ
解決策は簡単なようです。 2 番目の型パラメーターを導入するだけです。
template <typename T, typename T2> ??? max(T lhs,T2 rhs) { return (lhs > rhs)? lhs : rhs; } int main() { max(10.5f, 5.5); }
かんたんだよ!右?しかし、深刻な問題があります。 3 つのクエスチョン マークは戻り値の型として表示されますか?この問題は通常、関数テンプレートに複数の型パラメーターがある場合に発生します。戻り値の型は?.
この具体的なケースでは、戻り値の型は T、T2、または T と T2 から派生した Type R のいずれである必要がありますか?これは C++11 より前は難しい作業でしたが、C++11 では非常に簡単です。
私が考えているいくつかの解決策を次に示します。
// automaticReturnTypeDeduction.cpp #include <type_traits> template <typename T1, typename T2> // (1) typename std::conditional<(sizeof(T1) > sizeof(T2)), T1, T2>::type max1(T1 lhs,T2 rhs) { return (lhs > rhs)? lhs : rhs; } template <typename T1, typename T2> // (2) typename std::common_type<T1, T2>::type max2(T1 lhs,T2 rhs) { return (lhs > rhs)? lhs : rhs; } template <typename T1, typename T2> // (3) auto max3(T1 lhs,T2 rhs) { return (lhs > rhs)? lhs : rhs; } int main() { max1(10.5f, 5.5); max2(10.5f, 5.5); max3(10.5f, 5.5); }
最初の 2 つのバージョン max1
(1 行目) と max2
(2 行目) は type-traits ライブラリに基づいています。 3 番目のバージョン max3
(3行目) auto
の自動型推定を使用 .
max1
(1 行目):typename
std::conditional<(sizeof(T1) > sizeof(T2)), T1, T2>::type
タイプT1
を返します 、またはT2
それはもっと大きいです。 std::conditional はコンパイル時の三項演算子の一種です。max2
(line2):typename td::common_type<T1, T2>::type
型 T1 と T2 の共通型を返します。 std::common_type は、任意の数の引数を受け入れることができます。max3
(line3):auto
自明であるべきです。
typename
にイライラしているかもしれません 関数テンプレート max1
の戻り値の型の前 そしてmax2。 T1 と T2 は従属名です。従属名とは何ですか?従属名は基本的に、テンプレート パラメーターに依存する名前です。この場合、T1 と T2 が型であるというヒントをコンパイラに与える必要があります。基本的に、非型またはテンプレートにすることもできます。 3 番目の免責事項:依存型については別の投稿で書きます。 C++ Insights が提供するものを見てみましょう。テンプレートのインスタンス化のみを示します。プログラム全体を分析する場合は、次のリンクをたどってください:C++ Insights。 max1
(1 行目):戻り値の型を推測することしかできません。 return ステートメントでは、小さい方の型 (float) がdouble
に変換されます。 .max2
(2行目):max1
について 、 return ステートメントは戻り値の型についてのアイデアを提供します:float 値はdouble
に変換されます .
max3
(3 行目):これで、戻り値の型を明示的に確認できます。double
です .
次は?
今回の記事では、複数の型パラメーターを使用して、さまざまな型の関数引数の課題を解決しました。次回は、別のアプローチを取り、テンプレート引数を明示的に指定します。