問題の歴史:
C++にテンプレートが追加されたのは、汎用のアルゴリズムやデータ構造を効率的に実装するためです。最初から、テンプレートの使用を開発者にとって便利にするために、入力引数に基づく型の自動推論メカニズムが必要でした。
問題:
引数の推論メカニズムは常に明確ではありません:リファレンス、型のマスキング、部分特殊化、リファレンステンプレートパラメータ、定数とのあいだにあいまいさが生じます。時には、コンパイラが型を推論できないか、予期しない結果を出すことがあります。
解決策:
コンパイラは引数を解析し、cv-修飾子、リファレンス、ポインタのルールを考慮してテンプレートパラメータと対応させます。欠点や制約がある場合は、自動推論が不可能なときに明示的に型を指定する必要があります。
コードの例:
template<typename T> void func(T arg) { /* ... */ } func(10); // Tはintとして推論される func("abc"); // Tはconst char*として推論される // T argとT& argの違い: template<typename T> void printRef(T& arg); // 一時オブジェクトは受け入れない!
主な特徴:
もしテンプレート関数がT&を受け取った場合、一時オブジェクトを渡そうとするとどうなりますか?
コンパイラは型を推論できず、一時オブジェクトを非定数参照で渡すことはできません。コンパイルエラーが発生します。
template<typename T> void foo(T& arg); foo(42); // エラー!
配列に対する型推論はどのように機能しますか?
配列は値渡しの際にポインタに「デケイ」しますが、テンプレートが参照を受け取る場合、配列のサイズが保持され、これを利用してセーフサイズテンプレートを実装することがよくあります。
template<typename T, size_t N> void arraySize(T (&arr)[N]) { std::cout << N; } int x[10]; arraySize(x); // 10を出力する
テンプレート関数の呼び出し結果を保存する際にautoを使うことが常に正しいとは限らない理由は何ですか?
型推論時に、autoはconstやref修飾子を「スライス」することがあり、これが予期しないコピーや変更可能性のエラーを引き起こすことがあります。
auto x = funcReturningRef(); // xは値として保存され、参照ではない!
汎用ソート関数がT&を受け取り、ユーザーが一時オブジェクトを渡そうとすると、コードがコンパイルされず、エラーが開発者を困惑させます。
利点:
欠点:
ソート者はユニバーサル参照(T&&)を通じて実装され、std::forwardを使用することで、lvalueとrvalueの両方を効果的に処理でき、生産性と柔軟性が向上します。
利点:
欠点: