今日、多くの C++ 開発者にとって大きな驚きであるテンプレートに対する C++ コア ガイドライン ルールを完成させます。関数テンプレートの特殊化について書いています。
簡単に始めましょう。これは鳥瞰的な観点からのテンプレートの特殊化です。
テンプレートの専門化
テンプレートは、クラスと関数のファミリの動作を定義します。多くの場合、特殊なタイプまたは非タイプを特別に扱う必要があります。このユース ケースをサポートするには、テンプレートを完全に専門化します。クラス テンプレートは、部分的に特殊化することもできます。
一般的なアイデアを得るためのコード スニペットを次に示します。
template <typename T, int Line, int Column> // (1) class Matrix; template <typename T> // (2) class Matrix<T, 3, 3>{}; template <> // (3) class Matrix<int, 3, 3>{};
行 1 は、プライマリまたは一般的なテンプレートです。このテンプレートは少なくとも宣言する必要があり、部分的または完全に特殊化されたテンプレートの前に宣言する必要があります。 2 行目は、部分的な特殊化が続きます。行 3 は完全な特殊化です。
部分的および完全な専門化をよりよく理解するために、視覚的な説明を提示したいと思います。テンプレート パラメーターの n 次元空間について考えてみましょう。プライマリ テンプレート (1 行目) では、任意の型と 2 つの任意の int を選択できます。 2行目の部分特化の場合は型しか選べません。これは、3 次元空間が線に縮小されることを意味します。完全な特殊化とは、3 次元空間に 1 つのポイントがあることを意味します。
テンプレートを呼び出すとどうなりますか?
Matrix<int, 3, 3> m1; // class Matrix<int, 3, 3> Matrix<double, 3, 3> m2; // class Matrix<T, 3, 3> Matrix<std::string, 4, 3> m3; // class Matrix<T, Line, Column> => ERROR
m1 は完全な特殊化を使用し、m2 は部分的な特殊化を使用し、m3 はプライマリ テンプレートを使用しますが、定義がないためにエラーが発生します。
適切な特殊化を行うためにコンパイラが使用する 3 つの規則を次に示します。
<オール>さて、A が B よりも特化したテンプレートであることを説明する必要があります。 cppreference.com の非公式な定義は次のとおりです。「A は、B が受け入れるタイプのサブセットを受け入れます ".
最初の概要の後、関数テンプレートをもう少し深く掘り下げることができます
関数テンプレートの特殊化とオーバーロード
関数テンプレートを使用すると、テンプレートの特殊化の作業が簡単になりますが、同時に難しくなります。
- 関数テンプレートは完全な特殊化のみをサポートするため、簡単です。
- 関数のオーバーロードが発生するため、さらに難しくなります。
設計の観点から、テンプレートの特殊化またはオーバーロードを使用して関数テンプレートを特殊化できます。
// functionTemplateSpecialisation.cpp #include <iostream> #include <string> template <typename T> // (1) std::string getTypeName(T){ return "unknown type"; } template <> // (2) std::string getTypeName<int>(int){ return "int"; } std::string getTypeName(double){ // (3) return "double"; } int main(){ std::cout << std::endl; std::cout << "getTypeName(true): " << getTypeName(true) << std::endl; std::cout << "getTypeName(4711): " << getTypeName(4711) << std::endl; std::cout << "getTypeName(3.14): " << getTypeName(3.14) << std::endl; std::cout << std::endl; }
行 1 にはプライマリ テンプレートがあり、行 2 には int の完全な特殊化があり、行 3 には double のオーバーロードがあります。関数または関数テンプレートの値には興味がないので、スキップしました。たとえば、std::string getTypeName(double) です。さまざまな機能の使用は非常に快適です。コンパイラは型を推測し、正しい関数または関数テンプレートが呼び出されます。関数のオーバーロードの場合、関数のオーバーロードが完全に適合する場合、コンパイラは関数テンプレートよりも関数のオーバーロードを優先します。
しかし、投稿のタイトルで述べた大きな驚きはどこにあるのでしょうか?ここにあります。
T.144:関数テンプレートを特化しない
ルールの理由は非常に短く、関数テンプレートの特殊化はオーバーロードに参加しません。それが何を意味するか見てみましょう。私のプログラムは、Demiov/Abrahams のプログラム スニペットに基づいています。
// dimovAbrahams.cpp #include <iostream> #include <string> // getTypeName template<typename T> // (1) primary template std::string getTypeName(T){ return "unknown"; } template<typename T> // (2) primary template that overloads (1) std::string getTypeName(T*){ return "pointer"; } template<> // (3) explicit specialization of (2) std::string getTypeName(int*){ return "int pointer"; } // getTypeName2 template<typename T> // (4) primary template std::string getTypeName2(T){ return "unknown"; } template<> // (5) explicit specialization of (4) std::string getTypeName2(int*){ return "int pointer"; } template<typename T> // (6) primary template that overloads (4) std::string getTypeName2(T*){ return "pointer"; } int main(){ std::cout << std::endl; int *p; std::cout << "getTypeName(p): " << getTypeName(p) << std::endl; std::cout << "getTypeName2(p): " << getTypeName2(p) << std::endl; std::cout << std::endl; }
確かに、コードは非常に退屈に見えますが、我慢してください。プライマリ テンプレート getTypeName をインライン (1) で定義しました。行 2 はポインターのオーバーロードであり、行 3 は int ポインターの完全な特殊化です。 getTypeName2 の場合は、小さなバリエーションを作成しました。ポインターのオーバーロード (6 行目) の前に、明示的な特殊化 (5 行目) を配置しました。
この並べ替えは驚くべき結果をもたらします。
最初のケースでは int ポインターの完全な特殊化が呼び出され、2 番目のケースではポインターのオーバーロードが呼び出されます。何?この非直感的な動作の理由は、オーバーロードの解決が関数テンプレートの特殊化を無視するためです。オーバーロードの解決は、プライマリ テンプレートと関数で動作します。どちらの場合も、オーバーロードの解決により、両方のプライマリ テンプレートが見つかりました。最初のケース (getTypeName) では、ポインター バリアントの方が適しているため、int ポインターの明示的な特殊化が選択されました。 2 番目のバリアント (getTypeName2) では、ポインタ バリアントも選択されていますが、完全な特殊化はプライマリ テンプレートに属しています (4 行目)。その結果、無視されました。
次は?
この行を校正しているときに、私は考えを思いつきました。テンプレートは、より多くのサプライズに適しています。そのため、コア ガイドラインから少し離れて、いくつかを紹介します。このセリフに出くわしたら、覚えておいていただければ幸いです。
C++ の未来はテンプレートを語ります。したがって、彼らの言語についてもっと知ることは良いことです.