演算子オーバーロード
以下の実装はすべてのユニットテストに合格する。設計上の判断はコードコメントとして記載されている。
この構造体の関数の一部は、より効率的に実行するように実装することができる。さらに、分子と分母を正規化することも有益だろう。例えば、値20と60をそのまま保持する代わりに、これらの値を最大公約数で割り、分子と分母を1と3として保存することができる。そうしないと、オブジェクトに対するほとんどの操作で、分子と分母の値が増加してしまう。
import std.exception;
import std.conv;

struct Fraction {
 long num; // 分子
 long den; // 分母

 /* 便宜上、コンストラクタは分母にデフォルト値
 * 1を使用する。 */
 this(long num, long den = 1) {
 enforce(den != 0, "The denominator cannot be zero");

 this.num = num;
 this.den = den;

 /* 分母が常に正であることを保証することで
 * 一部の演算子関数の定義を簡略化できる。
 * */
 if (this.den < 0) {
 this.num = -this.num;
 this.den = -this.den;
 }
 }

 /* 一項 -: この分数の負の値を返す。 */
 Fraction opUnary(string op)() const
 if (op == "-") {
 /* 単に匿名オブジェクトを構築して返す。
 * */
 return Fraction(-num, den);
 }

 /* ++: 分数の値を1増やす。 */
 ref Fraction opUnary(string op)()
 if (op == "++") {
 /* ここでは'this += Fraction(1)'を使用することもできた。 */
 num += den;
 return this;
 }

 /* --: 分数の値を1減らす。 */
 ref Fraction opUnary(string op)()
 if (op == "--") {
 /* ここでは'this -= Fraction(1)'を使用することもできた。 */
 num -= den;
 return this;
 }

 /* +=: 右側の分数をこの分数に加える。 */
 ref Fraction opOpAssign(string op)(Fraction rhs)
 if (op == "+") {
 /* 加算式: a/b + c/d = (a*d + c*b)/(b*d) */
 num = (num * rhs.den) + (rhs.num * den);
 den *= rhs.den;
 return this;
 }

 /* -=: 右側の分数をこの分数から引く。 */
 ref Fraction opOpAssign(string op)(Fraction rhs)
 if (op == "-") {
 /* ここで、既に定義されている演算子+=と
 * 一項演算子-を使用している。代わりに、減算
 * 式を、上記の
 * +=演算子と同様に明示的に適用することもできる。
 *
 * 減算の公式: a/b - c/d = (a*d - c*b)/(b*d)
 */
 this += -rhs;
 return this;
 }

 /* *=: 分数を右辺で乗算する。 */
 ref Fraction opOpAssign(string op)(Fraction rhs)
 if (op == "*") {
 /* 乗算式: a/b * c/d = (a*c)/(b*d) */
 num *= rhs.num;
 den *= rhs.den;
 return this;
 }

 /* /=: 分数を右辺で割る。 */
 ref Fraction opOpAssign(string op)(Fraction rhs)
 if (op == "/") {
 enforce(rhs.num != 0, "Cannot divide by zero");

 /* 除算式: (a/b) / (c/d) = (a*d)/(b*c) */
 num *= rhs.den;
 den *= rhs.num;
 return this;
 }

 /* 二項演算子 +: この分数と右辺の分数を足し合わせた結果を。
 * 生成する。 */
 Fraction opBinary(string op)(Fraction rhs) const
 if (op == "+") {
 /* 二項演算子 *: 右辺の分数をコピーし、
 * そのコピーに左辺の分数を掛ける。 */
 Fraction result = this;
 result += rhs;
 return result;
 }

 /* 二項演算子 -: 右辺の分数からこの分数を
 * 引いた結果を返す。 */
 Fraction opBinary(string op)(Fraction rhs) const
 if (op == "-") {
 /* 既に定義されている-=演算子を使用する。 */
 Fraction result = this;
 result -= rhs;
 return result;
 }

 /* 二項演算子 *: この分数と右辺の分数を乗算した結果を
 * 返す。 */
 Fraction opBinary(string op)(Fraction rhs) const
 if (op == "*") {
 /* 既に定義されている *= 演算子を使用する。 */
 Fraction result = this;
 result *= rhs;
 return result;
 }

 /* 二項演算子 /: この分数と右辺の分数を除算した結果を
 * 返す。 */
 Fraction opBinary(string op)(Fraction rhs) const
 if (op == "/") {
 /* すでに定義されている/=演算子を使用する。 */
 Fraction result = this;
 result /= rhs;
 return result;
 }

 /* 分数の値をdoubleとして返す。 */
 double opCast(T : double)() const {
 /* 単純な除算。ただし、
 * long型の値を除算すると、小数点以下の値が失われるため、
 * ここでは'num/den'と書くことはできない。
 * */
 return to!double(num) / den;
 }

 /* 並べ替え順演算子:この分数が
 * 前にある場合は負の値を、この分数が
 * 後にある場合は正の値を、両方の分数の
 * 並べ替え順が同じ場合は0を返す。 */
 int opCmp(const Fraction rhs) const {
 immutable result = this - rhs;
 /* numはlong型であるため、int型に
 * 自動的に変換することはできない; 'to'(またはキャスト)によって
 * 明示的に変換する必要がある。 */
 return to!int(result.num);
 }

 /* 等価比較: 分数が等しい場合にtrue
 * を返す。
 *
 * この型では、等価比較を定義する必要があった。
 * なぜなら、コンパイラが生成する等価比較は、
 * オブジェクトが表す実際の値に関係なく、
 * メンバーを1つずつ比較してしまうからだ。
 *
 * たとえば、Fraction(1,2)
 * とFraction(2,4)の値は0.5だが、コンパイラが生成した
 * opEqualsは、メンバーの値が異なることを理由に、これらは等しくない
 * と判断する。 */
 bool opEquals(const Fraction rhs) const {
 /* opCmpの戻り値が0であるかどうかを確認する
 * だけで十分だ。 */
 return opCmp(rhs) == 0;
 }
}

unittest {
 /* 分母が0の場合はスローする必要がある。 */
 assertThrown(Fraction(42, 0));

 /* 1/3から始めよう。 */
 auto a = Fraction(1, 3);

 /* -1/3 */
 assert(-a == Fraction(-1, 3));

 /* 1/3 + 1 == 4/3 */
 ++a;
 assert(a == Fraction(4, 3));

 /* 4/3 - 1 == 1/3 */
 --a;
 assert(a == Fraction(1, 3));

 /* 1/3 + 2/3 == 3/3 */
 a += Fraction(2, 3);
 assert(a == Fraction(1));

 /* 3/3 - 2/3 == 1/3 */
 a -= Fraction(2, 3);
 assert(a == Fraction(1, 3));

 /* 1/3 * 8 == 8/3 */
 a *= Fraction(8);
 assert(a == Fraction(8, 3));

 /* 8/3 / 16/9 == 3/2 */
 a /= Fraction(16, 9);
 assert(a == Fraction(3, 2));

 /* 型'double'で同等の値を生成しなければならない。
 *
 * doubleはすべての値を正確に表現することはできないが、
 * 1.5は例外であることに注意。そのため、このテストが
 * この時点で適用されている。 */
 assert(to!double(a) == 1.5);

 /* 1.5 + 2.5 == 4 */
 assert(a + Fraction(5, 2) == Fraction(4, 1));

 /* 1.5 - 0.75 == 0.75 */
 assert(a - Fraction(3, 4) == Fraction(3, 4));

 /* 1.5 * 10 == 15 */
 assert(a * Fraction(10) == Fraction(15, 1));

 /* 1.5 / 4 == 3/8 */
 assert(a / Fraction(4) == Fraction(3, 8));

 /* ゼロで除算した場合はスローしなければならない。 */
 assertThrown(Fraction(42, 1) / Fraction(0));

 /* 分子の小さい方が先。 */
 assert(Fraction(3, 5) < Fraction(4, 5));

 /* 分母の大きい方が先。 */
 assert(Fraction(3, 9) < Fraction(3, 8));
 assert(Fraction(1, 1_000) > Fraction(1, 10_000));

 /* 値の小さい方が先。 */
 assert(Fraction(10, 100) < Fraction(1, 2));

 /* 負の値の方が先。 */
 assert(Fraction(-1, 2) < Fraction(0));
 assert(Fraction(1, -2) < Fraction(0));

 /* 値が等しいものは、両方とも<= and >=でなければならない。 */
 assert(Fraction(-1, -2) <= Fraction(1, 2));
 assert(Fraction(1, 2) <= Fraction(-1, -2));
 assert(Fraction(3, 7) <= Fraction(9, 21));
 assert(Fraction(3, 7) >= Fraction(9, 21));

 /* 値が等しいものは、等しくなければならない。 */
 assert(Fraction(1, 3) == Fraction(20, 60));

 /* 符号も値も等しいものは、等しくなければならない。 */
 assert(Fraction(-1, 2) == Fraction(1, -2));
 assert(Fraction(1, 2) == Fraction(-1, -2));
}

void main() {
}
この章で述べたように、文字列ミックスインを使用して、一部の演算子の定義を結合することができる。例えば、次の定義は4つの算術演算子をカバーしている。
/* 2進演算演算子。 */
Fraction opBinary(string op)(Fraction rhs) const
 if ((op == "+") || (op == "-") ||
 (op == "*") || (op == "/")) {
 /* この分数と右側の分数から新しい分数を作成する。右側の分数(分母)を新しい分数の分母に、
 * 左側の分数(分子)を新しい分子の分子にそれぞれ適用する。 */
 Fraction result = this;
 mixin ("result " ~ op ~ "= rhs;");
 return result;
}