出典は列挙するだけでなく、脚注などを用いてどの記述の情報源であるかを明記してください。記事の信頼性向上にご協力をお願いいたします。(2022年9月)
テンプレートメタプログラミング(英: template metaprogramming)は、メタプログラミング技法の一種であり、コンパイラがテンプレートを使って一時的ソースコードを生成し、それを他のソースコードと結合してコンパイルする方式である。テンプレートが出力するものは、コンパイル時の定数、データ構造、関数定義などがある。テンプレートの利用は言わばコンパイル時の実行である。この技法は様々な言語で使われている(C++、D言語、Eiffel、Haskell、ML、XLなど)。 メタプログラミング手法としてのテンプレート利用には2段階の操作が必要である。まずテンプレートを定義し、次にそれをインスタンス化しなければならない。テンプレートは生成すべきコードの一般化された形式を示し、インスタンス化によってそのテンプレートから具体的なソースコードが生成される。 テンプレートメタプログラミングは一般にチューリング完全であり、コンピュータプログラムで実行できることはテンプレートメタプログラムでも実行できる。 テンプレートはマクロとは異なる。マクロもコンパイル時に使われる機能で、文字列操作によってソースコードを生成する。一般的にソースコード中の字を置き換える形式のマクロ機能は、言語の意味とか型といったものを考慮できない(LISPのマクロはこの限りではない)。 テンプレートメタプログラムには変更可能な変数がない。つまり、変数は初期化時に一回代入を行うだけである。これは一種の関数型プログラミングと言える。実際、テンプレートの実装では制御構造や再帰呼び出しだけを実装していることが多い。 テンプレートメタプログラミングの文法はそのプログラミング言語本来のそれとだいぶ異なることが多いが、それでもテンプレートを使うのには理由がある。理由のひとつとしてジェネリックプログラミングの実装をするためということが挙げられる(似たようなコードをいくつも書かないようにする)。また、コンパイル時のコードの最適化の利点を最大限生かすためという理由もある。通常では最適化されない部分もテンプレートを使っていると最適化される場合がある。 「コンパイル時のプログラミング」とはどういう意味かを示すため、階乗関数の例を示す。以下はテンプレートを使わない場合のC++のコードである:int factorial(int n){ if (n == 0) return 1; return n * factorial(n - 1);}void foo(){ int x = factorial(4); // == (4 * 3 * 2 * 1) == 24 int y = factorial(0); // == 0! == 1} このコードは 4 と 0 の階乗を求めている。 テンプレートメタプログラミングとテンプレートの特殊化を再帰のために使って階乗の計算を実装すると、実行時に階乗の計算をせずにコンパイル時に計算を行うことができる:template <int N>struct Factorial{ enum { value = N * Factorial<N - 1>::value };};template <>struct Factorial<0>{ enum { value = 1 };};// Factorial<4>::value == 24// Factorial<0>::value == 1void foo(){ int x = Factorial<4>::value; // == 24 int y = Factorial<0>::value; // == 1} このコードは 4 と 0 の階乗をコンパイル時に計算し、その結果を定数のように扱う。 これら2種類のバージョンはどちらもプログラムの機能という意味では同じであるが、前者は階乗をプログラム実行時に計算しているのに対して、後者はそれをコンパイル時に計算する。しかし、このようにテンプレートを使うには、テンプレートのパラメータの値がコンパイル時に判っていなければならず、Factorial<X>::value は X がコンパイル時に決まっている場合にしか使えない。換言すれば、X はコンパイル時定数(リテラルや sizeof など)の呼び出し結果でなければならない。 上記の階乗の例はコンパイル時の最適化の一例であり、プログラム内で必要とされる階乗の値を予め計算して、定数としてコード内に組み込んでコンパイルを行う。これは実行時の性能とメモリ使用量の両面で効果がある。しかし、これはそれほど大きな最適化ではない。 もっと重要なコンパイル時の最適化として、テンプレートメタプログラミングを使ったコンパイル時のループ展開がある。ここでは n 次元のベクトルクラスの作成を例として示す(n はコンパイル時に分かっている)。n 次元ベクトルではループ展開によって性能が大きく改善される。ここでは加算演算子を例として示す。template<int dimension>Vector<dimension>& Vector<dimension>::operator+=(const Vector<dimension>& rhs){ for (int i = 0; i < dimension; ++i) value[i] += rhs.value[i]; return *this;} コンパイラがこのテンプレート関数をインスタンス化すると、次のようなコードが生成される:template<>Vector<2>& Vector<2>::operator+=(const Vector<2>& rhs){ value[0] += rhs.value[0]; value[1] += rhs.value[1]; return *this;} コンパイラは、テンプレートのパラメータ dimension がコンパイル時に定数なので、for ループをコンパイル時に展開できる。この手法の具体的実装例がBoost C++ Library等にある[1]。
テンプレートメタプログラミングの構成要素
テンプレートメタプログラミングの利用
コンパイル時のクラス生成
コンパイル時のコード最適化
Size:16 KB
出典: フリー百科事典『ウィキペディア(Wikipedia)』
担当:undef