CRTP (Curiously Recurring Template Pattern)の使い方
CRTPというC++で使われるテクニックがある。これは自分自身をテンプレートパラメータとしたテンプレートから派生してクラスを定義するものだ。これはこちらのページに詳しいが、今回はCRTPのもっとも便利だと思われる使い方を示してみようと思う。
CRTPを具体例は、以下のようだ。
template<class T> class CRTP_base{}; class A;//前宣言 class A:public CRTP_base<A>{ //省略・・・メンバいろいろ・・・ };
さあこれがなんの役に立つのか?・・・これは静的ポリモーフィズムを実現するのに役立つ。
たとえばクラスAとBがあり、これらが同じインターフェース(メンバ関数郡)をもたせ、これらを同様に扱いたいながらも異なる動作をさせたい。こんな時、普通は動的ポリモーフィズムを使う。例えば下のように。
class base{//インターフェース用基底クラス public: virtual void method_A() = 0;//仮想関数 virtual void method_B() = 0;//仮想関数 //仮想関数他にもいろいろ }; class A:public base{ public void method_A(){ /*実装*/ } void method_B(){ /*実装*/ } //他にもいろいろ実装 }; class B:public base{ public void method_A(){ /*実装*/ } void method_B(){ /*実装*/ } //他にもいろいろ実装 }; void function_p(base * p){//ポインタ版 //baseを継承したクラスのインスタンスのメンバを呼び出す。 } void function_r(base & r){//参照版 //baseを継承したクラスのインスタンスのメンバを呼び出す。 } // int main(){ base *bp; bp = new A();function_p(bp); delete bp;//Aのインスタンスを使う。 bp = new B();function_p(bp); delete bp;//Bのインスタンスを使う。 function_r( A() );//Aのインスタンスを使う。 function_r( B() );//Bのインスタンスを使う。 }
しかしながらこの方法はオブジェクトを返すような関数には、うまくいかない。例えばオブジェクトを足し算して返すよな関数だ。
返り値の型 plus(const base& lhs, const base & rhs){ //get_value() は足し算に必要なメンバであるとする。 return 返り値の型(lhs.get_value() + rhs.get_value());//何を返せばいい?返り値の型はbaseじゃないよね。 }
そこでCRTPをつかった静的なポリモーフィズム(実際には単なるオーバーロード)の出番となる。
template<class T> class CRTP_base{}; class A;class B;//前宣言 class A:public CRTP_base<A>{ //get_value() //省略 }; class B:public CRTP_base<B>{ //get_value() //省略 }; templateT plus(const CRTP_base & lhs, const CRTP_base & rhs){ return T(static_cast (lhs).get_value() + static_cast (rhs).get_value() );//get_value() は足し算に必要なメンバであるとする。 }
以上でクラスAおよびBに対して足し算関数が定義できた。
CRTPを使えば、同じインターフェースを持つクラスに対する関数を定義できる(可能になるというよりテンプレートに対し制限することができると言ったほうがいいだろう)。