xrea-banner xreaad

不定長 template 引数

namespace kilrey; / C 言語, C++ / Template / 不定長 template 引数

前のページ(部分特殊化)へ / 次のページ(小技)へ

可変長 template 引数についてです。 型安全な printf() などを作る場合に必要なのですが、 これも C++ の言語仕様で禁止されており実際には利用出来ません。 (ちなみに D 言語では可能だそうです。) この問題には完全な回避策は存在しません。 例えば

#include <iostream>
using namespace std;

void test() {
    cout << endl;
}

template<typename T0>
void test(T0 t0) {
    cout << t0;
    test();
}

template<typename T0, typename T1>
void test(T0 t0, T1 t1) {
    cout << t0;
    test(t1);
}

template<typename T0, typename T1, typename T2>
void test(T0 t0, T1 t1, T2 t2) {
    cout << t0;
    test(t1, t2);
}

int main() {
    test();
    test(0);
    test(0,1);
    test(0,1,2);
    return 0;
}
    

というように必要な数だけオーバーロードするという手法があります。 これは template を作成する時点で引数の上限を決めなくてはならず、 また各実装に繋がりがないので管理も面倒だという欠点があります。 スクリプトで生成すれば管理も比較的容易になりますが。

別の書き方では

#include <iostream>
using namespace std;

template <typename Car, typename Cdr>
struct Pair {
    Car car;
    Cdr cdr;
    Pair(Car car, Cdr cdr):
        car(car),
        cdr(cdr) {
    }
};

template <typename Car, typename Cdr>
Pair<Car, Cdr> cons(Car car, Cdr cdr) {
    return Pair<Car, Cdr>(car, cdr);
}

void test(void* vp) {
    cout << endl;
}

template <typename Car, typename Cdr>
void test(Pair<Car, Cdr> p) {
    cout << p.car;
    test(p.cdr);
}

int main() {
    test(cons(0, (void*)NULL));
    test(cons(0, cons(1, (void*)NULL)));
    test(cons(0, cons(1, cons(2, (void*)NULL))));
    return 0;
}
    

というようにリンク・リストとして引数を扱う手法もあります。 template 関数の部分特殊化やオーバーロードを組み合わせれば さまざまな条件に適用することが出来ますが、 呼び出し側でリンク・リストを生成するのが面倒です。 リンク・リストを生成するマクロを作るとやや手軽になりますが、 その場合はマクロへの引数に上限を設定する必要があります。 cons() を template 関数としてオーバーロードして 現実的に必要な引数の数だけ宣言してしまうのも良いでしょう。 全ての不定長 template 引数関数で多重オーバーロードするよりも こちらの方がだいぶ管理しやすいのではないかと思います。

不定長 template 引数については以上です。 マクロで言う __VA_ARGS__ のようなものが template にあるか、 逆にマクロを引数の数で特殊化出来るかすれば全て解決するのですが。

前のページ(部分特殊化)へ / 次のページ(小技)へ