テンプレートと継承

C++erにとって、テンプレートと継承は完全に必須であるものですが、どうにも効果的な使い方がよくわからない場合が多々あります。最近、これは確かに使える、というパターンがありましたので、ログ的に書いておこうと思います。

ちなみにこのパターン、Effecttive C++とかでも紹介されているので、ご存知の方も多いかと思います。

まず、純粋仮想関数を持つクラスを定義します。

class CalcBase {
public:
  int calc(int x, int y) = 0;
};

で、このクラスを基底クラスとするテンプレートを作成します。

template<class T>
class Calc : public CalcBase {
public:
  virtual int calc(int x, int y) {
      return T()(x, y);
  }
};

struct plus : public std::binary_function<int, int, int> {
  int operator()(int x, int y) {
      return x + y;
  }
};

std::cout << Calc<plus>().calc(1, 4) << std::endl;  // => 5

内容としてはシンプルですが、とりあえずこれで、2引数を取り、intを返す関数オブジェクトをテンプレート引数に取ることで、任意の演算が行えるようになります。

さて、お気付きでしょうが、CalcBaseを継承したCalcは、CalcBase* にアップキャストすることができます。

CalcBase* base = new Calc<plus>();
CalcBase* base2 = new Calc(minus)();

std::cout << base->calc(1, base2->calc(4, 2)) << std::endl; // => 3

こんな感じで、テンプレート引数のことを気にすることなく処理を行えるようになります。もちろん、テンプレートの内容を考える必要はありますが。
このようなイディオムを、「型名の隠蔽」と言うとか言わないとか。名前はどうでもいいんです(ぉ。

実際には、さらに関数テンプレートとクラステンプレートの生成を組み合わせてやることで、ロジックと型を分離することができ、使いようによっては、かなり処理が見やすくなります。
とりあえず久々の日記でした。もうちょっと頑張ろう。