D 始めました。 - template

since: 2003-06-16 update: 2004-07-10 count:

C++ や Eiffel なんかにあって、 Java にも導入されようとしている generics ですが、 D でももちろんサポートされています。 基本的なことがわかっていない方は、 K.INABA氏の解説を参照のこと。

おしながき

メタプログラミング

C++ 同様、template を使ってメタプログラミングができます。 template meta-programming については、 ここでは説明しません。

partial specialization を用いる C++ 的なコンパイルメタプログラミングはどうしても 再帰を多用したプログラミングになるということで、 再帰の例としてよく使われるけど全く例としてふさわしくないとして、 Efficient C++ で絶賛されているフィボナッチ数列を。 とりあえず C++ だと…

#include <stdio.h>
template <int lp>
struct p {
    enum { fib = p<lp-1>::fib   p<lp-2>::fib };
};
struct p<0> {
    enum { fib = 0 };
};
struct p<1> {
    enum { fib = 1 };
};
int main() {
    printf("%d\n", p<1000>::fib);
    return 0;
}

Dで書くと…

template p(int lp) {
    instance p(lp-1) p1;
    instance p(lp-2) p2;
    enum { fib = p1.fib + p2.fib };
}
template p(int lp : 0) {
    enum { fib = 0 };
}
template p(int lp : 1) {
    enum { fib = 1 };
}
int main(char[][] arg) {
    instance p(1000) pi;
    printf("%d\n", pi.fib);
    return 0;
}

フィボナッチ数列の 1000個目なんていうのは、 とっくに int の限界を越えてしまっていて、 両方ともオーバフローしまくってますが、 同じ結果が出ました。 素晴しいのがコンパイル速度です。

g++ -o partial partial.cc -ftemplate-depth-100000  3.57s user 0.09s system 98% cpu 3.723 total
dmd partial.d  1.04s user 0.04s system 99% cpu 1.088 total

と D の方が 3倍以上速くコンパイルができました。

自己言及的な…

この呼名は C++ラビリンスから。

D では多分、C++ とは違い、 こちらで述べられているような テンプレートの使い方はできないと思います。

というのは、クラスではなく名前空間をテンプレート化するので、 名前空間宣言時にはもちろんその中のクラス名は まだ宣言されてないからです。 これが無いと、いろいろできないことがあるので 少し困るところです…

嘘でした。できるらしいです。

class A : instance singleton(A).base {}

制約

無い機能ばっかり紹介するのもなんですが、 他の言語との比較は重要だと思うので。

仕様をコードに記述することを重視した言語、 Eiffel の総称では、総称パラメータに制約をかけることができます。 こちらから Eiffel のコードを引用させて頂くと…

class BIN_TREE [T -> COMPARABLE]  
...  
end -- BIN_TREE

と、このように COMPARABLE 継承クラスのみを 総称パラメータにとる、といった宣言を行うことができます。

これは非常に有用な機能で、例えば C++ の STL のイテレータでは、 イテレータを多数に分類して、それをコンセプトと呼び、 例えば std::sort に渡すイテレータは RandomAccessIterator でなければならない、 などとドキュメントに記述されています。 しかしこの記述はあくまでドキュメントへの記述であって、 コードに書いてあるわけではなく、 それによってコンパイラの挙動が変化するわけではなく、 例えば、

std::list<int> ints;
// ...

// エラー! list のイテレータは RandomAccess できない!
std::sort(ints.begin(), ints.end())

というようなコードに対して、コンパイラはわけのわからない エラーメッセージを大量に出します。

本題からそれまくっていますが、 コンパイル時に適切なエラーを出せないことによって、 コンパイルがうっかり通ってしまうと、 実行時エラーや、気がつかない間に効率を犠牲にしてしまう 恐れもあります。 C++ STL では list のイテレータに、例えば operator[] といったような、実装はできるけど非効率になってしまう 機能は提供しないことによって、 最悪でもコンパイルエラーが出るようにデザインされています。


home / index

全てリンクフリーです。 コード片は自由に使用していただいて構いません。 その他のものはGPL扱いであればあらゆる使用に関して文句は言いません。 なにかあれば下記メールアドレスへ。

shinichiro.hamaji _at_ gmail.com / shinichiro.h