テンプレート

C++では、変数に型やクラスを厳密に指定することで、プログラムの誤りをコンパイル時に発見できます。 しかし、この厳密な型の指定のために面倒なことが起きます。その一つは、型ごとに同じコードを書く必要があるということです。 テンプレートは、このような問題を解決して、更にC++の可能性を大きく広げる機能です。 これからはC++を使うということは、クラスと共にテンプレートを使うことになりつつあります。

関数テンプレート

まずは、以下の例を見てください。

プロジェクト名: template1

このプログラムは最大値を求めるものです。関数maxが主要部分です。実行結果は、

になります。ここで、maxはint型の数字を扱うことが出来ますが、charについても並べ替えを行いたいときにはどうしたらいいでしょう。 char用のmaxを記述すればよいということになります。以下の通りです。

プロジェクト名: template2

printf("最大値は:%c\n", m);

の%cを%dに変更してみて、それぞれの結果を確かめてください。

この場合、maxの引数としてchar型のデータが渡されているので新しく追加したchar型用のmaxが実行されます。

この仕組みを関数のオーバーロードといいます。

このプログラムでは、int用のmaxは使われていませんが残してあります。

ところで、charは一文字を表すことも出来ますから、char用のmaxを使って以下のようにも記述できます。

プロジェクト名: template2を修正

結果は、以下の様になります。'A'のような表記方法と数字の関係はここを参照してください。

更に、double型の並べ替えも書いてみましょう。プログラムは以下の様になるでしょう。

プロジェクト名: template2を修正

そして、結果は、

ここで、今作った3つのmaxを見てください。これらの関数は殆ど同じです。違いは、intchardoubleかだけです。

もし、maxに変更の必要が生じた場合、同じ変更作用を3回行う必要があります。このような状態は、好ましくありません。何とか一つのコードにまとめることは出来ないでしょうか。

こんなときに関数テンプレートが使えます。以下に関数テンプレートを使って書き換えたプログラムを示します。

プロジェクト名: template3

大文字のTがintchardoubleと入れ替わっています。このTのことをテンプレート引数と呼びます。そして、template <class T>の部分が、直後に続く関数maxが 関数テンプレートでTが型(クラス)を表すことを宣言しています。 このプログラムを実行すると正しく動作します。

それでは、色々な型を試して見ましょう。

プロジェクト名: template3を修正

 

一つのmaxで色々な型の最大値を求めることが出来ます。ここでは、int、char、 doubleだけを使っていますが、比較演算子 < が使える型であればどんな型でも使えます。

例えば、std::stringは<が定義されていますので使えます。この場合、maxで辞書順に最も大きい文字列を求めることが出来ます。 また、operator<を定義すれば自分で作ったクラスも使えます。このような演算子の定義について忘れてしまった人はここを参照してください。 以下に、演算子<の定義の例を示します。

プロジェクト名: template4

//---------------------------------------------------------------------------
#include <stdio.h>

//---------------------------------------------------------------------------
class MyInt
{
    public:
    int i;
    bool operator<(MyInt& m)
    {
       return i < m.i;
    }
};

int main(int argc, char* argv[])
{
    MyInt a,b;
    a.i = 3;
    b.i = 4;
    if(a < b) printf("a < b\n");
    return 0;
}

 

このクラスとmax関数の利用のサンプルです。

プロジェクト名: template4-2

テンプレートの仕組み

テンプレートのテンプレート引数は、コンパイル時に具体的な型に置き換えられます。つまり、

template <class T>
T max(T, T)
{
...中略...
}

のようなテンプレートは、プログラム中に例えば、 max(3 5);のような具体的な使用例がある場合、

コンパイル時にコンパイラが、上の使用例を発見した時に、その引数の型を調べて、

int max(int, int)
{
...中略...
}

が作られます。3や5がint型なので、Tがintに置き換わっ通常の関数が作られます。

うまくいかないとき(特別バージョン)

上で説明したテンプレート関数maxは、色々な型で正しく動きますが、うまく動かない型もあります。その一つの例がchar*です。

以下の例を見てください。

プロジェクト名: template5

このプログラムでは、辞書順では一番大きいのはtestですが、実行結果ではそうはならないはずです。これは、プログラムの

if(tmp < n[i])の部分に問題があるからです。

tmpもn[i]もchar*型なので、大小関係の比較は文字列の先頭のアドレスの比較になってしまうため、問題が起こります。

上のプログラムでは、"abc", "bcd", "test", "a"の順に記述されていますが、通常はこの順にメモリに配置されるので、"a"の先頭のアドレスが一番大きい事になります。 しかし、符号付の整数として比較されると逆にabcのアドレスが一番大きいことになります。

いずれにしても、文字列ではなくアドレスを比較してしまっているので、char*型の場合は上の例のテンプレート関数maxは使えないということになります。

このような場合は、以下のプログラムのようにchar*用のmaxを通常の関数として定義することで解決します。

プロジェクト名: template5を修正

今度は正しく答えが出ます。このようにchar*用のmaxのように特定の型のための関数をテンプレート関数に対して(ユーザ定義の)特別バージョン(user-defined specialization)と呼ぶことがあります。

特別バージョンのmaxで使われているstrcmp関数は文字列を比較するために使われています。引数として与えられた文字列が等しいとき0になり、第二引数が第一引数よりも辞書順で大きいとき負の値を返します。

詳しくはヘルプを参照してください。strcmpにカーソルを移動してF1を押下します。

 

テンプレートクラスに進みます。