天天看点

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;
}

主块代码;