天天看點

d使用d1的重載操作符

​​原文​​

上個月釋出的​

​2.100​

​​版本語言已完全删除​

​舊重載​

​​符号.但是,使用D出色的​

​元程式設計功能​

​​,可以編寫​

​插件模闆​

​​,來讓​

​D1​

​​風格​

​重載符号​

​繼續工作.

對比​​D1​​與​​D2​​重載符号

​重載​

​​符号用來​

​自定義​

​​(如+和-)操作符.在​

​D1​

​​中,是用​

​普通命名​

​​函數,如加法​

​opAdd​

​​或乘法​

​opMul​

​​.示例,用​

​整​

​​來表示​

​内部狀态​

​的結構類型:

struct S {
   int x;
   S opAdd(S other) {
      return S(x + other.x);
   }
   S opSub(S other) {
      return S(x - other.x);
   }
   S opMul(S other) {
      return S(x * other.x);
   }
   S opDiv(S other) {
      assert(other.x != 0, "除零!");
      return S(x / other.x);
   }//後面簡稱為`四塊代碼`.
}

void main() {//後面簡寫為`主塊代碼`
   S s1 = S(6);
   S s2 = S(3);
   assert(s1 + s2 == S( 9));
   assert(s1 - s2 == S( 3));
   assert(s1 * s2 == S(18));
   assert(s1 / s2 == S( 2));
   assert( s1 % s2  == S( 0));
   assert((s1 | s2) == S( 7));
    //...
}      

太多重複代碼.​

​D2​

​改進了.

struct S {
   int x;
   S opBinary(string op)(S other) {
      static if(op == "/" || op == "%")
         assert(other.x != 0, "除零!");
      return mixin("S(x ", op, " other.x)");
   } 
}
主塊代碼;      

注意,僅有​

​一個函數​

​​(​

​除法​

​​符号​

​略有​

​​不同),且處理​

​所有數學運算​

​​!​

​代碼​

​​更容易編寫,更不容易​

​出錯​

​​,也​

​更簡潔​

​.

别名符号

struct S {
   int x;
   四塊代碼;
   alias opBinary(op : "+") = opAdd;
   alias opBinary(op : "-") = opSub;
   alias opBinary(op : "*") = opMul;
   alias opBinary(op : "/") = opDiv;
//别名符号.
}      

注意,在此使用了​

​D元程式設計​

​​很酷的特性.​

​别名​

​​是​

​同名模闆​

​​,​

​特化​

​​了模闆參數,并用​

​模闆限制​

​過濾.

插件模闆

​插件模闆​

​​更是強大.

為了完成它,制定​​

​三個規則​

​​.

1,​​

​不關心​

​​是否按​

​D1​

​​風格​

​正确​

​​編寫​

​操作符​

​​.隻要​

​名字比對​

​​,就轉發給​

​他們​

​​.也不擔心​

​重載​

​​接受的​

​類型或參數​

​​,因為别名隻是​

​重寫​

​​名字.

2,該​​

​插件​

​​必須放在​

​類型​

​​末尾,否則,​

​編譯器​

​​可能不會分析​

​整個類型​

​​的成員(未來​

​D版本​

​​可能改變).

3,​​

​D​

​​禁止​

​插件和普通​

​​函數間的​

​重載​

​​,且​

​普通​

​​函數優先.是以,不要定義有​

​D2​

​​風格符号的特定名(如​

​opBinary​

​​).如果想要​

​D2​

​​符号,則反之.總之,不要混用​

​D1​

​​和​

​D2​

​​.

在​​

​插件​

​​模闆中編寫​

​opAdd​

​聲明,看看工作原理.

mixin template D1Ops() {
   static if(__traits(hasMember, typeof(this), "opAdd"))
      alias opBinary(op : "+") = opAdd;
//當有`opadd`時,别名此函數為左邊.
}      

這裡有很多​

​元代碼​

​​,我會一一解釋.

​​

​插件模闆​

​​聲明告訴​

​編譯器​

​​,這是​

​插件模闆​

​​.技術上,可​

​插件​

​​任意模闆,但聲明為​

​插件模闆​

​​,則它隻能用于​

​插件​

​​.

​​​靜如​​​是條件為真時執行.

​​

​__traits(hasMember,T,"opAdd")​

​​是僅當指定​

​T​

​​類型(這裡​

​插件​

​​至相應構類型)有​

​opAdd​

​​成員函數時才為真的​

​特殊條件​

​​.最後,​

​别名​

​​和之前寫的一樣.

現在,如何在​​

​類型​

​中使用它呢?

struct S {
   int x;
   四塊代碼;
   mixin D1Ops;
}      

使用别名,可處理現有​

​重載符号​

​​問題.再借助​​靜每一​​擴充.

mixin template D1Ops() {
   static foreach(op, d1;
     ["+" : "opAdd", "-" : "opSub", "*" : "opMul", "/" : "opDiv","%" : "opMod"]) {
//static foreach編譯時疊代普通運作時疊代元素.
      static if(__traits(hasMember, typeof(this), d1))
         alias opBinary(string s : op) = mixin(d1);//對每一個操作.
   }
}      
mixin template D1Ops() {
   static foreach(op, d1;
     ["+" : "opAdd", "-" : "opSub", "*" : "opMul", "/" : "opDiv","%" : "opMod"]) {
      static if(__traits(hasMember, typeof(this), d1))
         alias opBinary(string s : op) = mixin(d1);
   }
}

struct S {
   int x;
   四塊代碼;
   mixin D1Ops;
}

主塊代碼;