type aliasは型に別名を与えるもので、新たな型を定義するものではない。
なので、type aliasをした型で関数のオーバーロードを行うことはできない。
alias Dollar = int;
alias Euro = int;
alias Yen = int;
// type aliasでは全てint型として扱われるのでオーバーロードできない
Yen exchange(Dollar money) {
Yen yen;
yen = 120 * money;
return yen;
}
Yen exchange(Euro money) {
Yen yen;
yen = 140 * money;
return yen;
}
新しく型を定義するにはstruct
かclass
を使う。Dでは委譲を簡単に行うためのalias thisというものが用意されている。この機能を使うと、委譲先の型に暗黙に変換できるようになるため、同じように扱うことができる。
struct Dollar {
int delegater;
alias delegater this;
}
struct Euro {
int delegater;
alias delegater this;
}
struct Yen {
int delegater;
alias delegater this;
}
// structで異なる型を定義しているのでオーバーロードできる
Yen exchange(Dollar money) {
Yen yen;
yen = 120 * money; // alias thisでint型と同様に扱うことができる
return yen;
}
Yen exchange(Euro money) {
Yen yen;
yen = 140 * money;
return yen;
}
もちろん、templateを使うことでコードの重複を排除できる。
struct Another(T) {
T delegater;
alias delegater this;
}
// Another!intで定義した新たな型に別名を与えているのでオーバーロード可能
alias Dollar = Another!int;
alias Euro = Another!int;
alias Yen = Another!int;
...
しかし、alias thisにはよくわからない点がある。次のコードはコンパイルが通らない。
struct myint {
int x;
alias x this;
}
// 初期化
myint hoge = 100;
myint foo() {
return 42; // 関数の戻り値
}
コンストラクタ構文(であってるのか?)やキャストで明示的に指定すればできる。Classes - D Programming Languageによるとalias thisで委譲した型は委譲先の型に暗黙に変換することができるとあり、委譲先の型から暗黙に変換できるとは書いていないので、こういう動作でも別に良いのだが……。
それならそれで、変数宣言と代入を分けて書くとコンパイルが通るのは不思議である。構文的には初期化子のExpInitializerはAssignExpressionと等しいので、初期化子に書くのと変数宣言と代入を分けて書くのに違いがでるとは思えない。また関数の戻り値は構文的にはExpressionであり、式は関数の戻り値の型に暗黙に変換されると書かれている。
ソースコードを読めばいいのか……まあ、暇ができたら……