テンプレートメタプログラミング (条件分岐)

条件分岐

コンパイル時に条件分岐を行うためのテンプレートの使い方です。
テンプレート引数に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;

100
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();

10
A....
A!
class Thenやclass Elseにはintやfloatなどの型を入れることもできるので、例えばテンプレート引数の条件分岐によってunsigned charまたはshortの変数を用意するコードを記述することもできます。

	IF<true,unsigned char, short> waveData[100];