テンプレートと継承
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
こんな感じで、テンプレート引数のことを気にすることなく処理を行えるようになります。もちろん、テンプレートの内容を考える必要はありますが。
このようなイディオムを、「型名の隠蔽」と言うとか言わないとか。名前はどうでもいいんです(ぉ。
実際には、さらに関数テンプレートとクラステンプレートの生成を組み合わせてやることで、ロジックと型を分離することができ、使いようによっては、かなり処理が見やすくなります。
とりあえず久々の日記でした。もうちょっと頑張ろう。