TMP : Template Meta Programming

Template Metaprograms
前回に引き続いて再びC++Reportから。今度はテンプレートメタプログラミング(TMP)の記事を読んでみた。

TMPとは、簡単にいうと、テンプレートを特殊化するプロセスを応用してコンパイルと同時に実行結果を生成するテクニックといっていいのだろうか。コンパイラをあたかもインタプリタのように振る舞わせることで、通常のマクロ展開よりも遥かに強力なプリプロセッサ機能を利用できるようになる。
TMPでは条件分岐やループなどの制御構造をテンプレートの特殊化と再帰呼び出しで表現する。この意味においてTMPは関数型言語と良く似てるといえるかもしれない。例えば、ユークリッドの互除法をTMPで書くと次のようなプログラムになる。

template <unsigned int a, unsigned int b>
struct which_t {
  enum {
    greater = (a > b) ? a : b,
    less = (a < b) ? a : b
  };
};

template <unsigned int a, unsigned int b>
struct gcd_t {
  enum {
    p = which_t<a, b>::greater,
    q = which_t<a, b>::less,
    value = gcd_t<q, p % q>::value
  };
};

template <unsigned int a>
struct gcd_t<a, 0> {
  enum {
    value = a
  };
};

このプログラムでは、アルゴリズムの基本となるループをテンプレート(gcd_t)の再帰呼び出しによって記述し、終了条件をこのテンプレートを特殊化することで定義している。計算結果を取得するには同じく引数を渡してテンプレートを特殊化すればいい。

std::cout << gcd_t<51, 136>::value << std::endl;

テンプレートの特殊化はコンパイル時に行われるので上のコードは実際は次のコードと等価となる。

std::cout << 17 << std::endl;

このようにTMPを使うと本来実行時に発生する計算コストをコンパイル時に移動させることができるので、パフォーマンスチューニングのテクニックとして大変有効に思われる。しかし、その適用範囲は期待する程広くはなさそうだ。本記事ではバブルソートを例に関数版とTMP版の2種類の実装を用意して性能評価を行っていて、関数呼び出しのコストが発生しない分、TMP版は関数版よりも予想通り速い結果となっている。しかし、入力の大きさが極小さい場合を除くと両者の間にはさほど大きな性能差を認められない。結局のところ、TMPによるチューニングはあくまで奥の手にすべきであって肝心なのはやはりアルゴリズムの性能なんだということだろう。

ちなみにこの記事の初出は1995年。C++の標準仕様が初めてISOに承認されるちょうど3年前になる。こうしてブログを書くついでに昔の記事を掘り起こしてみると、標準化に向けて活発だった当時のC++コミュニティの勢いが伺い知れてなんだか感慨深い。