トレイト・クラス

トレイト・クラスと呼ばれる、C++のイディオムを使うと、型を返す関数(型関数)が作れます。型関数はテンプレートを利用して実現されていて、コンパイル時に実行されることになります。下の例だと、構造体input_typeがトレイト・クラスにあたり、InputType(型,引数の数)が型を返します。

template<typename T, int i>
struct input_type;
#define InputType(T, i) typename input_type<T, i>::type
#define Domain(F) InputType(F, 0)

Domain(F)のFは引数が同じ型の関数ポインタか関数オブジェクトが入り、Domain(F)は関数の引数の型を返します(数学で言うところの定義域になる)。input_typeを型ごとに定義することでDomain(F)が返す型を分岐させることができます。例えば、2乗を計算する関数オブジェクトstruct sqと関数ポインタsqureのそれぞれに対してinput_typeを定義します。

template<typename T>
struct sq {
  T operator() (const T& x) {
    return x * x;
  }
};
template<typename T>
struct input_type<sq<T>, 0> {
  typedef T type;
};
template<typename T>
struct input_type<T(*)(const T&), 0> {
  typedef T type;
};
template<typename T>
T square(const T& x) {
  return x * x;
}

このようにして定義した型関数は、テンプレート関数の中で使うことができます。

template<typename F, typename N>
Domain(F) power_unary(Domain(F) x, N n, F f) {
  while(n != N(0)) {
    x = f(x);
    n = n - N(1);
  }
  return x;
}
#include <cassert>
int main(void) {
  int n ;
  int x ;
  for(n = 1; n < 5; n++) {
    for(x = 2; x < 5; x++) {
      int tmp = power_unary(x, n-1, sq<int>());
      assert(power_unary(x, n, sq<int>()) == tmp * tmp);
      assert(power_unary(x, n, square<int>) == tmp * tmp);
    }
  }
  return 0;
}

ここで使ったコードの完全なものはこのリンクから得ることができます。