C++言語解説:2-4.テンプレート
2002-07-28 |
[C++ Index Top] [Prev] [Next] |
[概要] プログラムコードを再利用するために用意された機能がテンプレートです。テン プレートの構造について学習します。 [構成]・汎用関数 * 型が違うだけなら * 汎用関数のオーバーロード * template指定のオーバーロード * 仮引数の指定 ・汎用クラス * 汎用クラスの定義 * 非汎用引数指定 * 型のデフォルト指定 ●汎用関数 ◆型が違うだけなら ・C++には関数のオーバーロードと呼ばれる機能があります。これは以前(1-6章.関数 の機能)説明しましたが、例えば (a + b)^2 の演算を行う関数CalFunc()を各型のデータへ対応させるため int CalFunc(int a, int b); double CalFunc(double a, double b); のように、関数をオーバーロード定義しました。 ・この場合、関数の中身は全く変わりません。恐らく「もっと汎用的に書ければ簡単 なのに」と思った人もいることでしょう。C++では汎用関数(generic function)と呼 ばれる機能でこれを実現することができます。 ・汎用関数は、以下の形式で定義します。 template <class 型引数> 戻値型 関数名(仮引数リスト){・・・} 型引数の部分には、まさに型が入ります。List1に汎用関数の例を示します。 [List1.汎用関数の例] ┌───────────────────────────────────┐ #include <iostream> using namespace std; template <class T> T CalFunc(T a, T b) //template文と関数名定義に改行あってもOK { retuen (a*a + b*b + 2*a*b); } int main() { cout << CalFunc(1,2) << endl; cout << CalFunc(1.1, 2.3) << endl; return 0; } └───────────────────────────────────┘ ┌───────────────────────────────────┐ [出力結果] 9 11.56 └───────────────────────────────────┘ ・templateキーワードと関数名定義の間に改行が入っても問題有りません。汎用関数 の型引数は、カンマ演算子により複数定義できるので、このような場合は改行表現 することが多くなります。 [List2.汎用関数(複数型)の例] ┌───────────────────────────────────┐ #include <iostream> using namespace std; template <class T1, class T2> void TypeDisp(T1 a, T2 b) { cout << "T1:" << a << endl; cout << "T2:" << b << endl; return; } int main() { TypeDisp(1.0, 'a'); TypeDisp("ABCD", 10L); return 0; } └───────────────────────────────────┘ ┌───────────────────────────────────┐ [出力結果] T1:1 T2:a T1:ABCD T2:10 └───────────────────────────────────┘ ◆汎用関数のオーバーロード ・汎用関数では型に従って関数が生成されます。しかし「ある型の場合は実装を変え たい」というケースもあるでしょう。この場合、特別Versionの汎用関数を作ること でオーバーロード(*1)ができます。以下の書式で定義します。 template<> 戻値型 関数名<特定型>(引数リスト){・・・} ・このような関数を原著ではユーザ定義特別版(user-defined specialization)と呼ん でいます。List3に例を示します。 [List3.汎用関数の特別Version(明示的特定)] ┌───────────────────────────────────┐ #include <iostream> using namespace std; template <class T> T Func(T a) //通常汎用関数 { cout << "Template : "; return a; } template<> int Func<int>(int a) //int型特別Version { cout << "Special : "; return a; } int main() { cout << Func("ABCD") << endl; cout << Func(10) << endl; return 0; } └───────────────────────────────────┘ ┌───────────────────────────────────┐ [出力結果] Template : ABCD Special : 10 └───────────────────────────────────┘ ◆template指定のオーバーロード ・関数オーバロードと同様に、template指定自体をオーバーロードすることも可能で す。List4に例を示します。 [List4.template指定のオーバーロード] ┌───────────────────────────────────┐ #include <iostream> using namespace std; template <class T> void Func(T a) { cout << "Template1 : "; cout << "a:"<< a << endl; return; } template<class T1, class T2> void Func(T1 a, T2 b) { cout << "Template2 : "; cout << "a:"<< a << " "; cout << "b:"<< b << endl; return; } int main() { Func('a'); Func(10); Func(10.5, 5); Func("ABCD", 6); return 0; } └───────────────────────────────────┘ ┌───────────────────────────────────┐ [出力結果] Template1 : a:a Template1 : a:10 Template2 : a:10.5 b:5 Template2 : a:ABCD b:6 └───────────────────────────────────┘ ◆仮引数の指定 ・汎用関数の仮引数指定には、特定の型を混在させることができます。List5に例を示 します。 [List5.仮引数の特定型混在指定] ┌───────────────────────────────────┐ #include <iostream> using namespace std; template <class T> void Func(T a, int i_a) { cout << "Template1 : "; cout << "a:"<< a << " "; cout << "int:" << i_a << endl; return; } int main() { Func("ABCD", 10); Func(5,20); return 0; } └───────────────────────────────────┘ ┌───────────────────────────────────┐ [出力結果] Template1 : a:ABCD int:10 Template1 : a:5 int:20 └───────────────────────────────────┘ ・デフォルト引数などを使用した際に、関数指定が曖昧になる可能性については、関 数オーバーロードを使用するときと同じようなケアが必要です。 [List6.指定が曖昧 (*コンパイルエラーになる)] ┌───────────────────────────────────┐ #include <iostream> using namespace std; template <class T> void Func(T a, int i_a = 0) { cout << "Template1 : "; cout << "a:"<< a << " "; cout << "int:" << i_a << endl; return; } template <class T> void Func(T a) { cout << "Template2 : "; cout << "a:"<< a << " "; return; } int main() { Func("ABCD"); return 0; } └───────────────────────────────────┘ ●汎用クラス ◆汎用クラスの定義 ・クラスについても関数と同様に、汎用クラス(generic class)を定義することができ ます。汎用クラスは以下のように記述します。 template <class 型引数> class クラス名 {・・・}; ・汎用クラスの場合は関数と違い、オブジェクト生成時に引数が与えられるとは限ら ないので、型指定を伴ったオブジェクト定義を以下の形式で行います。 クラス名<型名> オブジェクト名; ・classブロックの外で定義するメンバ関数については、クラス名部に型指定記述が追 加されます。 temaplte <class 型引数> 戻値型 クラス名<型引数>::関数名(引数リスト) {・・・} List7に汎用クラスの使用例を示します。 [List7.汎用クラスの使用例] ┌───────────────────────────────────┐ #include <iostream> using namespace std; template <class T> class AddSum { T* mt_data; int mi_index; public: AddSum(int i_index); ~AddSum(); void InData(int i_index, T t_data); T GetSum(); }; template <class T> AddSum<T>::AddSum(int i_index) { mi_index = i_index; mt_data = new T [mi_index]; for (int i=0; i<mi_index; i++){ *(mt_data+i) = (T)0; } return; } template <class T> AddSum<T>::~AddSum() { delete[] mt_data; return; } template <class T> void AddSum<T>::InData(int i_index, T t_data) { if (i_index < mi_index) *(mt_data+i_index) = t_data; return; } template <class T> T AddSum<T>::GetSum() { T t_sum = (T)0; for (int i=0; i<mi_index; i++) { t_sum += *(mt_data+i); } return t_sum; } int main() { AddSum<int> int_obj(3); int_obj.InData(0, 10); int_obj.InData(1, 20); int_obj.InData(2, 30); AddSum<double> double_obj(3); double_obj.InData(0, 10.2); double_obj.InData(1, 11.6); double_obj.InData(2, 24.93); cout << int_obj.GetSum() << endl; cout << double_obj.GetSum() << endl; return 0; } └───────────────────────────────────┘ ┌───────────────────────────────────┐ [出力結果] 60 46.73 └───────────────────────────────────┘ ・List7では1個の型引数を利用していますが、カンマ演算子により複数の型引数を指 定できることは、汎用関数と変わりません。 ・また、その他の機能 + 特定型に対する特別Versionを作成できる点、 + templateのオーバーロードが適用できる 等の点についても汎用関数と同様に使用することができます。 ◆非汎用引数指定 ・汎用クラスでは、型指定部に非汎用の引数を指定することができます。例えば以下 のように template <class T, int i_a> class クラス名 {・・・}; この場合、オブジェクト定義は クラス名<double, 10> A; のように記述することができます。 ・非汎用引数部はデフォルト引数を適用することもできます。例えば template <class T, int i_a=10> class クラス名 {・・・}; であれば クラス名<double> A; //省略時 i_a = 10; と記述することができます。 ◆型のデフォルト指定 ・汎用クラスの場合、型指定が省略された場合のデフォルト型を用意することができ ます。デフォルト型は以下のように指定します。 temaplte <class T=デフォルト型名> class クラス名 {・・・}; ・その際、以下のようにオブジェクト指定を行うことが可能です。 class<> オブジェクト名; List9に使用例を示します。 [List9.クラスのデフォルト型指定使用例] ┌───────────────────────────────────┐ #include <iostream> using namespace std; template <class T=int> class AddSum { T* mt_data; int mi_index; public: AddSum(int i_index); ~AddSum(); void InData(int i_index, T t_data); T GetSum(); }; : : //省略 List7と同様 : int main() { AddSum<> int_obj(3); //型指定省略 int_obj.InData(0, 10); int_obj.InData(1, 20); int_obj.InData(2, 30); AddSum<double> double_obj(3); double_obj.InData(0, 10.2); double_obj.InData(1, 11.6); double_obj.InData(2, 24.93); cout << int_obj.GetSum() << endl; cout << double_obj.GetSum() << endl; return 0; } └───────────────────────────────────┘ ┌───────────────────────────────────┐ [出力結果] 60 46.73 └───────────────────────────────────┘ (*1)ここで紹介した特別Version(明示的特定)以外に、単純な関数のオーバーロードも適用 できます。特別Versionは最近のC++で追加された機能であり、それまでは関数オーバ ロードで対応していたからです。 [Revision Table] |Revision |Date |Comments |----------|-----------|----------------------------------------------------- |1.00 |2002-07-28 |初版 |1.01 |2002-07-30 |リンク追加 [end] |
Copyright(C) 2002 Altmo
|
[C++ Index Top] [Prev] [Next] |