Programs / 最終更新時間:2003年11月29日 02時49分57秒

概要

C++言語のテンプレート機能を紹介します。

目次

テンプレートとは

テンプレート。C++を使っていながら、昔はその存在すら知りませんでした。

テンプレートとは、クラスに何かの機能を加えたいときに直接そのクラスに書きこむのではなく、また親クラスからの継承をやるのでもなく、クラスに別クラスを持たせるオブジェクトコンポジション(委譲)でもなく、別のやり方で簡単に機能を付け加える――そういうものです。[1]

  • [1]省略のあまりかなり誤解をまねく表現ですが。

その付け加えたい機能を使うクラスがひとつしかないなら、クラスに直接書けばいいことです。ですから、テンプレートを使うのはある機能を加えたいクラスが複数あるときです。

「え? それをするならクラスの継承じゃないの??」

そう思います。いや、同意しても仕方ない。ではここでVC++のInfoViewerを使って「テンプレート」を検索してみましょう。ありますね、テンプレート。

詳しく知りたい方はそこを見てください――って、いやちゃんと説明もします。でも、いまからする説明はかなりいいかげん。

テンプレートは便利です

テンプレートが便利なのは、あるデータ型(クラス)に関する処理をデータ型を特定せずに書けることに違いない。

データ型は何でもよい(データ型に依存したプログラムを書かない限り)
    ↓
どんなデータ型(クラス)にも、その機能を付け加えることができる

ということです。データ型ごとにクラスを作成し、同じ種類のコードを書かなくてすむ……と。

こんなクラスがあったとします

ではここで、簡単な例ですが、下のようなクラスがあったとします。

class CTestInt
{
   int n;
   void plus1(int &x);
};

plus1関数は引数として与えられた参照型変数 x に 1を足すというもので、下がそのコードです。int nは使わないけど、これくらいは洒落で定義する……と。

void CTestInt::plus1(int &x)
{
   ++x;
}

そして、ここに

class CTestFloat
{
   float n;
};

というクラスと、

class CTestDouble
{
   double n;
};

という、何に使うのかまったく不明のクラスがあったとしましょう。しかし、名前から察するに、CTestFloatはfloat型を、CTestDoubleはdouble型を扱うクラスのようです。

そのとき、あなたは思いました。この2つのクラスにもCTestIntクラスにある、plus1関数みたいなのがほしい……!(仮ですよ、仮)

plus1関数のコードは少量なので、これから、float型やdouble型で同じ内容の関数を書き加えていっても、別に苦にならないかもしれません。――が、それは間違いのもとになります。今後plus1関数の修正を行おうとしたときにも同じ作業が必要になりますし、ここはひとつ、クラスの継承でやってみましょう。

まず、基底クラスとして……って、おい。これクラスの継承じゃ、出来ないじゃないか!

クラステンプレートを使う

そうです! そんなときこそテンプレートなのです。まずクラステンプレートを作ります。[2]

  • [2]関数テンプレートでもかまいません。例としてクラステンプレートを扱っているだけです
template <class T>
class CCalculate
{
   plus1(T &x)
};

そして、plus1関数のコードをテンプレートを使用して書くと、

template <class T>
void CCalculate::plus1(T &x)
{
   ++x;
}

こうなります。普通のクラスと違うのは、template <class T>なんていうのを付けることと、データ型を T と書いていることだけですね。このクラステンプレートをクラスへ適用して生成したクラスなら、どんなクラスでもそのクラスのメンバ関数として、plus1が使用できるようになるのです。

クラステンプレートを適用する

では、テンプレートをクラスへ適用する、その方法とは?

簡単です。この場合のクラステンプレートはCCalculateという大層な名前のついたクラスです――これをCTestFloatクラスに適用し、クラスtestを生成するには、

// クラステンプレート名<クラス名> 生成するクラス名;
CCalculate<CTestFloat> test;

とやるだけ。これだけで、

int a = 0;
test.plus1(a)

なんてことが出来るようになります。これでCTestIntもCTestFloatも全部、plus1を使えるようになった!……生成時に指定すれば。

「生成時にいちいち指定するのがちょっと……」という方は、

typedef CCalculate<CTestFloat> CCalucTestFloat;

としておき、

CCalucTestFloat test;

とやるとよいです。

まとめ

どうでしょう? 今回の例はあまりテンプレートを使わなくてもよさそうなもの(プリプロセッサのマクロを使って書くとかもできるし――)でしたが、知っておけば何かと便利なものなので、知らなかったという方はこれを機にひとつ覚えてみては。

テンプレートを使った便利なライブラリとしては、

などが有名どころです。

マクロとどう違うの? かというと、templateは型を保持しているという点が重要です。
もうvoid型のポインタを使ったり、無理にキャストする危険を犯す必要はないのです。

関数テンプレートの使い方

またテンプレートには関数テンプレートというものもあります。使い方は非常に簡単ですので、それも載せておきます。

template <class T>
void add(T &a, T &b)
{
   a += b;
}

これで a の値に b の値を代入する関数ができました。この場合は同じ型同士でないと駄目です。

普通の関数を呼ぶように、そのまま使えます。

注意事項

テンプレートを不用意に使うと実行ファイルの容量が大きくなったり、デバッグがしにくくなることもあることがあります。容量制限の厳しい開発(ゲームなど)では使いどころを見極めたり、展開が膨大にならないように使い方に注意するとよいでしょう。