テンプレートメタプログラミング

定数

コンパイル時に定数を代入する方法を考えてみます。

Step0

コンパイル後の式がこのようになるためにはどうすればいいでしょうか?

cout << 1 << endl;

enumで適当な変数名を定義すれば、コンパイル時にプリプロセッサが勝手に数字に置き換えてくれます。

enum { 
	value = 1
};

void test(){
	cout << value << endl;
}
Step1

グローバルはあまり好ましくないので適当なクラススコープ内で宣言してみます。これでもC1::valueが1に置き換わります。

struct C1{
	enum { 
		value = 1
	};
};
void test(){
	cout << C1::value << endl;
}
Step2

他のクラスでも宣言することができます。C1::valueを呼べば1に、C2を呼べば2に置き換わります。

struct C1{
	enum { 
		value = 1
	};
};
struct C2{
	enum { 
		value = 2
	};
};
void test(){
	cout << C1::value << endl;	//1
	cout << C2::value << endl;	//2
}
Step3

Nに置き換えられるよう拡張して、1の場合を呼び出すこともできます。ここでテンプレートが登場します。

template <int N>
struct C{
	enum { 
		value = N
	};
};
void test(){
	cout << C<1>::value << endl;	//1
	cout << C<2>::value << endl;	//2
}

計算式

Step0

コンパイル後の式がこのようになるためにはどうすればいいでしょうか?

cout << 5 + 1 << endl;

Nを表示するためのテンプレートが既にできているので、あとは"+1"を追加すれば良いです。

template <int N>
struct C{
	enum { 
		ret= N + 1
	};
};
void test(){
	cout << C<5>::ret << endl;	//5 + 1
}
Step1

コンパイル後の式がこのようになるためにはどうすればいいでしょうか?

cout << 4 + 3 + 2 + 1 << endl;
template <int N>
struct C{
	enum { 
		ret = 4 + 3 + 2 + N 
	};
};
void test(){
	cout << C<1>::ret << endl;	// 4 + 3 + 2 + 1
}

上のようにももちろん書くことができますが、もう少し工夫してみます。1,2,3,4ごとに別のクラス(orスコープ)を作成して、各クラスの値retが入れ子になるように構成してみます。1,2,3,4がランダムな数ではなくて、再帰的な規則性をもった数列だからこそ使える手法です。

struct Sum1{
	enum { 
		ret = 1
	};
};
struct Sum2{
	enum { 
		ret = 2 + Sum1::ret
	};
};
struct Sum3{
	enum { 
		ret = 3 + Sum2::ret
	};
};
struct Sum4{
	enum { 
		ret = 4 + Sum3::ret
	};
};

こうすると、Sum2、Sum3、Sum4が同じ構造をしていることが分かります。テンプレート化すると次のようになります。

template <int N>
struct Sum{
	enum { 
		ret = N + Sum<N-1>::ret
	};
};

Sum1だけ定義している値がことなるので、これは特別な場合として考えましょう。

template <>
struct Sum<1>{
	enum { 
		ret = 1
	};
};

今はN=4の場合なので、次のコードでコンパイル時に計算式を生成できることが分かります。

void test(){
	cout << Sum<4>::ret << endl; // 4 + 3 + 2 + 1
}

inline関数を使う

処理内容をコンパイル時に展開して記述するinline関数を使うこともできます。

inline int Sum1(){
	return 1;
}
inline int Sum2(){
	return 2 + Sum1();
}
inline int Sum3(){
	return 3 + Sum2();
}
inline int Sum4(){
	return 4 + Sum3();
}

テンプレート化すると以下のようになります。

template<int N>
inline int Sum(){
	return N + Sum<N-1>();
}
template<>
inline int Sum<1>(){
	return 1;
}

呼び出すときはもちろん関数として呼び出します。

	cout << Sum<4>() << endl;