C++によるプログラミング入門補足4
テンプレートについて
こんにちは。今回は、テンプレートというものについて、簡単に説明します。テンプレートが難しいという話も聞きますが、具体例を見れば、それがどういうものかはすぐわかると思います。
実用的なクラスを作ると、その説明で読む方も書く方も疲れてしまうと思います。(いきなり、、、)そこで、実用性は考えない、簡単なクラスで考えることにします。たとえば、下のようなクラスを考えましょう。
class Iremono { int data; public: void set_data(int d){ data=d; } int get_data(){ return data; } };
このクラスはdataのいれものです。このクラスについては、次のようなプログラムができるでしょう。
//templ_sample0.cpp
#include <iostream>
using namespace std;
class Iremono
{
int data;
public:
void set_data(int d){ data=d; }
int get_data(){ return data; }
};
int main()
{
Iremono x;
x.set_data(3);
cout << x.get_data() << endl;
}
例によって、こんなクラスが何の役に立つのか、このプログラムはなんなんだ、、、なんて言わないでください。まあ、こういうものがあったとしましょう。
ここで、ある時、気が変わって、Iremonoのデータdataを整数intではなく、実数floatにしたくなったとします。(つまり小数もありにしたくなったという意味です。あまり難しく考えないでください。)この位なら、簡単に書きかえられる、、、でしょうが、一度書いたものを、書き直すのはしゃくです。(反C++的です。ちょっと言い過ぎかな。)だいたい、はじめにクラスIremonoを書いたときに、このような変更の予想が、なぜできなかったかと反省しきりです。
こういう問題を最初から考えて対処する方法はいくつかありますが、テンプレートを使うのがひとつの方法です。まず、答えを見てください。最初のクラスの定義を次のようにしておけばよかったのです。
template<class T> class Iremono
{
T data;
public:
void set_data(T d){ data=d; }
T get_data(){ return data; }
};
前のクラスと比べてみてください。最初にtemplate<class
T>とある他は、intがTに変わっているだけです。実は、intとかfloatとか、具体的に書く代わりに、何かわからないけど、後でここには何か「型」(基本型やクラス)が来るよ、それをとりあえずTと呼ぶよ、、、と言っているのです。そう言っているのが最初のtemplate<class
T>なのです。
そう思ってもう一度見てください。今度は、少しわかったような気になりませんか?
これをどう使うか、下の例を見れば、もう少しわかるかと思います。
//templ_sample1.cpp
#include <iostream>
using namespace std;
template<class T> class Iremono
{
T data;
public:
void set_data(T d){ data=d; }
T get_data(){ return data; }
};
int main()
{
Iremono<int> seisu;
Iremono<float> jissu;
seisu.set_data(3);
//整数を代入
jissu.set_data(1.5);
//実数を代入
cout << seisu.get_data() << endl;
cout << jissu.get_data() << endl;
}
mainの中にあるように、Iremono<int>と書くと、それは「(クラステンプレートIremonoで)Tをintにしたクラス」になり、Iremono<float>と書くと「Tをfloatにしたクラス」になるのです。この話のすごいところは、Tには自分(や他のプログラマ)が定義したクラスを入れても良い、ということです。
テンプレートのパラメータは、
template<class T, class U, ...> class Iremono
{
... TやUなどを使う
};
のように、複数にすることもできます。
テンプレートには、関数テンプレートというものもあります。これは、関数の引数や戻り値の型をあとから決めるものです。たとえば、次のようなものがそうです。
template<class T> T sanbai(T a){
return a * 3;
}
これは、引数の3倍を戻す(グローバル)関数のテンプレートです。Tはあとで決められる型ですが、これもクラステンプレートのときのように、<>で指定することができます。この関数テンプレートを使うサンプルが次のように書けます。
//templ_sample2.cpp
#include <iostream>
using namespace std;
template<class T> T sanbai(T a){
return a * 3;
}
int main()
{
cout << sanbai<int>(3)
<< endl;
cout << sanbai<float>(1.5)
<< endl;
}
sanbai<int>と書くと、「(関数テンプレートsanbaiで)引数の型と戻り値の型をintにした関数」になり、sanbai<float>は、「引数の型と戻り値の型をfloatにした関数」になるわけです。
ただし、引数の型をテンプレートのパラメータにした場合、(普通は)実際に与えた引数からその型が判別されます。その場合は、<>で型を与えなくてもよいことになっています。実際、上のプログラムは、下のように書いても、同様に動作します。
//templ_sample3.cpp
#include <iostream>
using namespace std;
template<class T> T sanbai(T a){
return a * 3;
}
int main()
{
cout << sanbai(3) <<
endl; //Tを指定していないがOK
cout << sanbai(1.5)
<< endl; //Tを指定していないがOK
}
関数テンプレートも、クラステンプレートのときと同様に、複数のパラメータを持つことができます。