C/C++ Programming

C/C++について


再帰テンプレート
激しく今更感がありますが, 自分自身を呼び出すテンプレートのことです. テンプレートメタプログラミング(template metaprogramming)というのが一般的なようです. 再帰呼び出しの終了にはテンプレートの特殊化(template specialization)を利用します.
例1. 1からnまでの和
1からnまでの和をコンパイル時に計算するコードを見てみます.
template<int n>
struct sum1toN {
    enum {
        value = sum1toN<n - 1>::value + n
    };
};

template<>
struct sum1toN<0> {
    enum {
        value = 0
    };
};

int main()
{
    printf("sum=%d\n", sum1toN<10>::value);
    return 0;
}

実行すると

    sum=55

と表示されます. この55はコンパイル時に決定されるので実行時には定数です.

    sum1toN<3>::value

と書くと, それは

    sum1toN<2>::value + 3

と展開され, 更にあと二回sum1toNが呼び出さて

    sum1toN<0>::value + 1 + 2 + 3

となったところで, テンプレートの特殊化sum1toN<0>が呼び出されて停止します. 結果,

    0 + 1 + 2 + 3

となり, コンパイラがこれを計算して6に置き換えます.

例2. 対数の計算
nが与えられたときに, log2(n)を超えない整数を計算してみましょう. つまり, 与えられたnを何回2で割れるかを求めます.
template<int n>
struct ilog2 {
    enum {
        value = ilog2<n / 2>::value + 1
    };
};

template<>
struct ilog2<0> {
    enum {
        value = -1
    };
};

繰り返し2で割り, もう割り切れなくなったところでテンプレートの特殊化が呼び出されます.

    ilog2<1>::value
    ilog2<2>::value
    ilog2<3>::value
    ilog2<4>::value
    ilog2<5>::value
    ilog2<7>::value
    ilog2<8>::value

はそれぞれ0, 1, 1, 2, 2, 2, 3という値に置き換えられます.

例3. 2進数定数
C/C++では2進数定数を直接使えないのでそれの代用. ただし232 - 1 = 4294967295 と10桁なので指定可能な数値は10bitが限界. それを超えると嘘の値が出るので注意.
元ネタはC++ Compile-Time Binary Constants. klx C++ Librariesでも紹介されています.
template<int n>
struct binary {
    enum {
        bit = n % 10,
        value = bit + (binary<n / 10>::value << 1)
    };
};

template<>
struct binary<0> {
    enum {
        bit = 0,
        value = 0
    };
};

binary<11110>::valueとすれば30となるわけですな.

例4. 整数の浮動小数表現
与えられた自然数nのfloat型表現(ここでは32bitと仮定)を求めます. x86系のC言語表記ならfloat f = n;として*(int*)&fを得るということ. アセンブリ言語使ってるとたまに便利. 去年作ってみたのを再アップしました. たとえば0なら0, 1なら0x3F800000, 16777215なら0x4B7FFFFFに変換してくれます.
template<int x>
struct positive_float_int2binary {
    enum {
        log2_x = ilog2<x>::value,
        exponent = (log2_x + 127) << 23,
        f = x - (1 << log2_x),
        mantissa = (log2_x <= 23) ? (f << (23 - log2_x)) : (unsigned(f) >> (log2_x - 23)),
        value = exponent | mantissa
    };
};

例2で作ったilog2を使っています. 名前がいまいちですが, まあ気にしない. 与えられた自然数xに対してx-ilog2<x>が浮動小数表現としての仮数部になります. 浮動小数の格納形式も参考にしていただければ. float型の制約上224あたりが限界です. コード内では負数などにも対応できるよう拡張しています.


御意見は光成滋生<herumi@nifty.com>までお願いします