テンプレート関数
汎用関数
ここでは、C++言語の高い柔軟性の実現の一つ
テンプレートについて、説明していきます
テンプレートの概念は、オブジェクト指向で名高い Java にすらない、すばらしい機能です
プログラムでは、内部処理は同じだがデータ型が異なる関数が頻繁に出てきます
とくにアルゴリズムにおける関数では、その様なケースが多いです
全てのデータ型の関数をオーバーロードでサポートするのも一つの方法ですが
この作業は面倒で、しかもソースの可読性も低下させてしまいます
そこで、この作業を全てコンパイラに任せてしまうのが汎用関数なのです
汎用関数は、各データ型を受け取ることができます
しかし、オーバーロードとは異なり内部処理は同じです
このような汎用関数をテンプレート関数とも呼び
次のように template キーワードを用いて宣言します
template <class type> function-declarator
function-declarator は、関数の宣言を行います
type は、関数が受け取る汎用データ型のです
もちろん、そんなものが実際に存在するわけではなく
コンパイル時に、それぞれの型に対応した関数が生成され
type は各データ型に置きかえられます
#include <iostream>
using namespace std;
template <class X> void println(X out) {
cout << out << '\n';
}
int main() {
println("Kitty on your lap");
println(10);
println(1.052);
return 0;
}
このプログラムの println() は汎用関数です
受け取ったデータを出力するだけなので、内部処理はデータ型に関係ありません
main() 関数内部で、いろいろな型で println() を呼び出していますが
汎用関数 println() は、それぞれのデータ型に対応した println() をサポートしています
template <class X> void println(X out) | テンプレート関数
|
---|
| | | | | | インスタンシエート
|
---|
void println(char *) | void println(int) | void println(double) | 生成された関数
|
---|
上の図のように、使用されるデータ型に合わせてテンプレート関数はオーバーロードします
数学関数などで内部処理が同じだが、受け取るデータ型は整数や浮動小数点などが考えられる場合
テンプレートを用いた関数を使うと非常に簡単です
ソース上のテンプレート関数を、各データ型専用の関数に変換する工程をインスタンシエートと呼び
コンパイル後に各データ型用に作られた関数を生成された関数と呼びます
ここで使われる class キーワードは typename キーワードでもかまいません
比較的、一般的に使われるのは class キーワードです
template <typename type> function-declarator
また、テンプレートの定義は、関数の定義とは別のところで行うこともできます
ただし template 文と関数の間に別の文を挿入することはできません
#include <iostream>
using namespace std;
template <typename X>
X max(X var1 , X var2) {
if (var1 > var2) return var1;
else return var2;
}
int main() {
cout << max(10 , 100);
return 0;
}
template の引数の並びで、classの代わりに typename キーワードを使っています
また、複数の型を受け取る汎用関数の生成も可能です
tenplate の引数の並びで、カンマで型を区切り汎用データ型を複数定義します
#include <iostream>
using namespace std;
template <class X1 , class X2> void println(X1 var1 , X2 var2) {
cout << var1 << " : " << var2 << '\n';
}
int main() {
println(1000 , 56.025);
println('A' , "Di Gi Gharat");
println("Kitty on your lap" , "LOVE HINA");
return 0;
}
こうすることで、異なるデータ型の組み合わせを自由に受け取れます
最後の println() のように、同じデータ型でも問題はありません
テンプレートのオーバーライド
テンプレートは型に応じて関数をオーバーロードすることはわかりました
あらゆるデータ型に対応できる、内部処理が同じ関数の生成を自動化できます
しかし、例えばほとんどのデータ型の処理は同じだが
整数型の時だけ、違う処理を行いたいような場合もあると思います
そのような場合は汎用関数をオーバーロードしてしまいます
つまり、明示的に目的の型でオーバーロードしてしまうことで、テンプレートを隠します
#include <iostream>
using namespace std;
template <class X> void println(X out) {
cout << out << '\n';
}
void println(float out) {
cout << 'F' << out << '\n';
}
int main() {
println("Kitty on your lap");
println(56.025f);
println(100.01);
return 0;
}
汎用関数 println() は、float 型を受け取った時のみ違う処理を行います
通常は汎用関数 println(X) を呼び出しますが
この関数は println(float) を明示的にオーバーロードすることで
float 型を受け取った時のみ、明示した関数を呼び出します
もっとも、データ型によって処理が異なる関数の生成は
テンプレートではなくて関数のオーバーロードが専門分野です
テンプレートの中のある特定のデータ型でのみ処理が異なるという場合に用います
template
template < [typelist] [ , [ arglist]] > declaration
パラメータ化した汎用関数、または汎用クラスを指定します
typelist - class または typename キーワードで、汎用データ型を定義します
arglist - 汎用データ型の引数リストです
declaration - 関数またはクラスを宣言します
typename
typename identifier
テンプレートの定義で用います
未知の識別子が型であることをコンパイラに通知します
このキーワードの代わりに class キーワードを用いることも可能です