テンプレートメタプログラミング (条件分岐)
条件分岐
コンパイル時に条件分岐を行うためのテンプレートの使い方です。
テンプレート引数にtrueまたはflaseを指定すると、それぞれ100,200が代入されるようにしてみます。
//テンプレート引数にtrueを入れた場合 cout << 100 << endl;//100 //テンプレート引数にfalseを入れた場合 cout << 200 << endl;//200
まずはtrueバージョンとfalseバージョンのクラスを定義して、数値を分離します。
struct IF_true{ enum { ret= 100 }; }; struct IF_false{ enum { ret= 200 }; }; void test{ //テンプレート引数にtrueを入れた場合 cout << IF_true::ret << endl;//100 //テンプレート引数にfalseを入れた場合 cout << IF_false::ret << endl;//200 }
テンプレート化
さらにtrueとfalseをテンプレート引数にしたクラスを作成します。まずboolをテンプレート引数としたIFクラスを作ります。そのあと引数がtrueとfalseの特殊な場合を定義しておきます。
template <bool Con> struct IF{ }; template <> struct IF<true> { enum { ret = 100 }; }; template <> struct IF<false> { enum { ret = 200 }; };
利用するときは、テンプレート引数としてtrueまたはfalseを指定します。
cout << IF<true>::ret <<endl; cout << IF<false>::ret <<endl;
200
条件分岐の応用
条件によってさまざまなクラスを呼び出すことができるようにしてみましょう。コンパイル時にテンプレート引数としてtrueを指定したときにはクラスAを、falseを指定したときにはクラスBを展開するようにしてみます。次のようにクラスA,Bでメンバ変数や定数、関数などが定義されているとします。メンバがinline関数やstaticな定数の場合は、処理をコンパイル時に行うことができます。
struct A{ /*クラスAで定義されているもの*/ static const int val = 10; static void statFunc(){ cout << "A...." << endl; } void func(){ cout << "A!" << endl; } //インスタンス化して利用できるinlineメンバ関数 }; struct B{ /*クラスBで定義されているもの*/ static const int val = 20; static void statFunc(){ cout << "B...." << endl; } void func(){ cout << "B!" << endl; } //インスタンス化して利用できるinlineメンバ関数 }; void test(){ //テンプレート引数にtrueを入れた場合 Aで定義されているものを展開する cout << A::val <<endl; A::statFunc(); A a; a.func(); //テンプレート引数にfalseを入れた場合 Bで定義されているものを展開する cout << B::val <<endl; B::statFunc(); B b; b.func(); }
条件によって呼び出すクラスを変化させるには次のようにtypedefを使って定義すれば実現することができます。
struct IF_true{ typedef A ret; }; struct IF_false{ typedef B ret; }; void test(){ //テンプレート引数にtrueを入れた場合 Aで定義されているものを展開する cout << IF_true::ret::val <<endl; IF_true::ret::statFunc(); IF_true::ret trueObj; trueObj.func(); //テンプレート引数にfalseを入れた場合 Bで定義されているものを展開する cout << IF_false::ret::val <<endl; IF_false::ret::statFunc(); IF_false::ret falseObj; falseObj.func(); }
テンプレート化その1
IF_trueとIF_falseをテンプレート化します。
template <bool Con> struct IF; template <> struct IF<true>{ typedef A ret; }; template <> struct IF<false>{ typedef B ret; };
trueの場合の利用例は下のようになります。
cout << IF<true>::ret::val <<endl; IF<true>::ret::statFunc(); IF<true>::ret Obj; Obj.func();
falseを入れた場合も同様です。
cout << IF<false>::ret::val <<endl; IF<false>::ret::statFunc(); IF<false>::ret Obj; Obj.func();
テンプレート化その2
上の例では、trueのときはクラスA、falseのときはクラスBしか展開できません。いわばクラスAとB専用のIFなので改めてIF_ABと書くことにします。これをさらに拡張して、どんなクラスでも展開できるようにしてみましょう。
template <bool Con> struct IF_AB; template <> struct IF_AB<true>{ typedef A ret; }; template <> struct IF_AB<false>{ typedef B ret; };
クラスそのものをテンプレート引数にすれば全てのクラスに対応することができます。
template <bool Con, class Then,class Else> struct IF; template <class Then,class Else> struct IF<true,Then,Else>{ typedef Then ret; }; template <class Then,class Else> struct IF<false,Then,Else>{ typedef Else ret; };
//条件分岐の対象クラスをA,Bに設定して、テンプレート引数にtrueを入れた場合 cout << IF<true,A,B>::ret::val <<endl; IF<true,A,B>::ret::statFunc(); IF<true,A,B>::ret Obj; Obj.func();
A....
A!
IF<true,unsigned char, short> waveData[100];