読者です 読者をやめる 読者になる 読者になる

ゆとりーなの日記

日記的な事を書いて行くと思はれる

世紀末プログラミング

id:yappy_t氏が何やら世紀末プログラミングに就いて言及していたので、私がこれに触れないわけがないわけですよ。
世紀末プログラミングとは、テンプレートメタプログラムのことを指すようです。コンパイル時に色々解決してしまう恐ろしいプログラム手法です。コンパイルさえ通れば結構実行時にバグを持ち込まないという特徴があるような気が個人的にはします。
一番有名なのは階乗を求めるテンプレートでしょう。

template <int N>
struct factorial {
  static const int value = N * factorial<N - 1>::value;
};

template <>
struct factorial<0> {
  static const int value = 1;
};

int main() {
  const int a = factorial<3>::value; // 6
  return 0;
}

流石に今時enumハックは不用であろうということでこれについては触れません。上のコードは関数の再帰で実装された階乗の関数をテンプレートに置き換えたものと考える事が出来ます。世紀末プログラミングとは、通常の関数で言うところの引数がテンプレート引数、戻り値がvalue、条件分岐を特殊化で表現したものなわけですね。
世紀末プリグラミングは上の様な数値計算だけでなく型計算にも使われます。こちらの場合は戻り値はtypeという名のtypedefのことが多いようです。

template <bool Cond, typename Then, typename Else>
struct type_if {
  typedef Then type;
};

template <typename Then, typename Else>
struct type_if<false, Then, Else> {
  typedef Else type;
};

int main() {
  type_if<true, int, char>::type a;  // int
  type_if<false, int, char>::type b; // char
  return 0;
}

上の例では第一引数の真偽で型をスイッチできます。
更に強力な例は第一引数の真偽でオーバーロードすることですかね。

template <bool Cond>
struct enable_if {
  typedef void type;
};

template <>
struct enable_if<false> {};

template <typename T>
void foo(T t, typename enable_if<sizeof(T) == 4>::type * = nullptr) {
  std::cout << "sizeof 4" << std::endl;
}

template <typename T>
void foo(T t, typename enable_if<sizeof(T) != 4>::type * = nullptr) {
}

int main() {
  const int a = 0;
  const char b = 0;
  foo(a); // 一般的な処理系では多分「sizeof 4」が出力
  foo(b);
  return 0;
}

SFINAEと呼ばれるテクが使われています。
他の例としてはconstをひっつけたり取り外したりするクラステンプレートとかも書けます。

template <typename T>
struct add_const {
  typedef const T type;
};

template <typename T>
struct remove_const {
  typedef T type;
};

template <typename T>
struct remove_const<const T> {
  typedef T type;
};

int main() {
  add_const<int>::type a = 0;          // const int
  remove_const<int>::type b = 0;       // int
  remove_const<const int>::type c = 0; // int
  return 0;
}

constを外す方は、Tにconstが付いていた時だけconstを外すを実現するためにconstが付いた場合のみ特殊化してconstを外すという処理を書きます。ポインタやvolatileも似たような感じに作れます。参照のみは参照にadd_referenceするとおかしなことになるのでこれだけは特殊化で分岐しておきます。

template <typename T>
struct add_reference {
  typedef T & type;
};

template <typename T>
struct add_reference<T &> {
  typedef T type;
};

これでint &がTに来た時にint &&にならないように出来ます。
以上が簡単な世紀末プログラミングの説明ですかね。これはほんの入り口にすぎないというのと地味に0xにはここで紹介した殆どの技法が標準ライブラリに入って使えるようになるというのはまさに世紀末に相応しい言語なわけですね。