1年ぶりの更新
本当は8回でやめる予定だったけど
C++にはまだまだ便利な機能があるので紹介するお!?(^ω^)
第9回はテンプレート関数、テンプレートクラスについての紹介
テンプレートとは穴空きという意味で
穴を埋めることで意味を成すものを指します
テンプレートを使うことで同じ機能を持つ関数クラスの記述をより省略でき
プログラムの汎用性を増すことができます。
たとえば、今までは同じ機能を持つ関数は
オーバーロードなどで
(ここでは足し算を行う関数)
#include <iostream> using namespace std; int Add(int a,int b){ return a + b; } double Add(double a,double b){ return a + b; } int main(){ int resultInt = Add(5,10); double resultDouble = Add(5.5,10.5); cout << resultInt << endl; cout << resultDouble << endl; return 0; }
のように2つ関数を書く必要がありました
しかしテンプレートを上手に使えば関数の数を減らすことができます(ぉ
テンプレートを定義するには
関数、クラスの先頭に
template <class T>
のように書く必要があります
ここでTという型を関数を使用するときに指定します
Tという型名は変えてもかまいません
ここでclassと書いているけど
クラス以外にも使えるので
気持ち悪い人は次のように書いても大丈夫です
template <typename T>
では実際にみたほうが早いので
先ほどの例でテンプレート関数を使ってみましょう
#include <iostream> using namespace std; template <class T> T Add(T a,T b){ return a + b; } int main(){ int resultInt = Add<int>(5,10); double resultDouble = Add<double>(5.5,10.5); cout << resultInt << endl; cout << resultDouble << endl; return 0; }
ここでポイントはテンプレート関数を使用するときに
Add<int>(5,10); Add<double>(5.5,10.5);
のように使用時に型を指定していることです。
これにより適切な型がコンパイル時に埋め込まれ、切り替えが行われます。
また、テンプレートには複数の型を指定できる書き方もあります
たとえば2つの場合は
template <class T,class U>
のように書きます
#include <iostream> using namespace std; template <class T,class U> T Add(T a,U b){ return a + b; } int main(){ double add = Add<double,int>(5.5,1); cout << add << endl; return 0; }
使い方は
Add<double,int>(5.5,1);
のように2つ型を指定する必要があります。
テンプレートの型3つ以上はまぁ・・・だいたい想像がつくだろうから書きません(ぁ
テンプレート関数については以上!
テンプレート関数と同様にテンプレートクラスも定義することができます。
#include <iostream> using namespace std; template <class T> class MyTemplate{ public: // テンプレートクラスの関数 T Add(T a,T b); // テンプレートクラスの関数(インライン) T Sub(T a,T b){ return a - b; } // クラスのテンプレート関数(インライン) template <class U> U Mul(U a,U b){ return a * b; } // クラスのテンプレート関数(複数テンプレート) template <class U,class S> S Div(U a,S b); }; // テンプレートクラスの関数実装 template <class T> T MyTemplate<T>::Add(T a,T b){ return a + b; } // クラスのテンプレート関数(複数テンプレート) template <class T> template <class U,class S> S MyTemplate<T>::Div(U a,S b){ return a/b; } int main(){ MyTemplate<int> temp;// クラス宣言時にTの型を決定する int add = temp.Add(5,2); double sub = temp.Sub(10,5); float mul = temp.Mul<float>(2.5f,3.0f); // Uに埋め込むのでテンプレートクラスの型と関係ない double div = temp.Div<float,double>(4.0f,1.5f); // U,S複数に型を埋め込み計算する cout << add << endl; cout << sub << endl; cout << mul << endl; cout << div << endl; return 0; }
ポイントは
クラス宣言時に型が決定するところです。
MyTemplate<int> temp;// クラス宣言時にTの型を決定する
ほかについては特に注意する点はないかな
クラス内でさらにテンプレート関数を定義することもできます
その場合でも使う場合は、テンプレート関数と同じ用に使えばおk
temp.Mul<float>(2.5f,3.0f);
テンプレートクラスのテンプレート関数の実装を分けて書くには
(そもそも、テンプレートクラスのテンプレート関数をあまり見たことがないけど・・・)
// クラスのテンプレート関数(複数テンプレート) template <class T> template <class U,class S> S MyTemplate<T>::Div(U a,S b){ return a/b; }
のように2回templateを書きます
テンプレートの欠点は関数やクラスを呼び出す部分でコンパイル時に型が決定されるため
分割コンパイルで実装部分をcpp側に書けないという欠点があります
(実装を同じファイルに書く必要があるため、必然的にヘッダーファイルに実装を書く必要がある)