我正在设计我自己的编程语言(称为 Lima,如果你在 www.btetrud.com 上关心它),我正在努力思考如何实现运算符重载。我决定在特定对象上绑定(bind)运算符(它是一种基于原型(prototype)的语言)。 (它也是一种动态语言,其中 'var' 就像 javascript 中的 'var' - 一个可以保存任何类型值的变量)。

例如,这将是一个带有重新定义的 + 运算符的对象:

x =  
{  int member 
 
   operator +  
    self int[b]: 
       ret b+self 
    int[a] self: 
       ret member+a 
} 

我希望它的作用相当明显。当 x 既是右操作数又是左操作数时,该运算符被定义(使用 self 表示这一点)。

问题是当您有两个对象以像这样的开放式方式定义运算符时该怎么办。例如,在这种情况下您会做什么:
A =  
{ int x 
  operator + 
   self var[b]: 
    ret x+b 
} 
 
B =  
{ int x 
  operator + 
   var[a] self: 
    ret x+a 
} 
 
a+b   ;; is a's or b's + operator used? 

所以这个问题的一个简单答案是“好吧,不要做出模棱两可的定义”,但它并不是那么简单。如果您包含一个具有 A 类型对象的模块,然后定义了 B 类型对象怎么办。

您如何创建一种语言来防止其他对象劫持您想要对您的运算符(operator)执行的操作?

C++ 将运算符重载定义为类的“成员”。 C++ 如何处理这样的歧义?

请您参考如下方法:

大多数语言会优先考虑左边的类。我相信,C++ 根本不允许您重载右侧的运算符。当您定义 operator+ ,当这种类型在左侧时,您正在定义加法,对于右侧的任何东西。

事实上,如果你允许你的 operator + 是没有意义的。当类型在右侧时工作。它适用于 +,但考虑 -。如果类型 A 定义 operator -以某种方式,我做 int x - A y,我不想要 A 的 operator -被调用,因为它会反向计算减法!

在 Python 中,它有更广泛的 operator overloading rules , 反方向有单独的方法。例如,有一个 __sub__当此类型位于左侧时重载 - 运算符的方法,以及 __rsub__当此类型位于右侧时,它会重载 - 运算符。这类似于在您的语言中允许“自我”出现在左侧或右侧的能力,但它会引入歧义。

Python 优先考虑左边的东西——这在动态语言中效果更好。如果 Python 遇到 x - y ,它首先调用x.__sub__(y)看看 x知道如何减去 y .这可以产生一个结果,或者返回一个特殊值 NotImplemented .如果 Python 发现 NotImplemented被退回,然后尝试另一种方式。它调用 y.__rsub__(x) ,它会被编程知道 y在右手边。如果这也返回 NotImplemented ,然后是 TypeError引发,因为类型与该操作不兼容。

我认为这是动态语言的理想运算符重载策略。

编辑:稍微总结一下,你有一个模棱两可的情况,所以你真的只有三个选择:

  • 优先考虑一侧或另一侧(通常是左侧)。这可以防止具有右侧重载的类劫持具有左侧重载的类,但不能反过来。 (这在动态语言中效果最好,因为方法可以决定它们是否可以处理它,并动态地推迟到另一种。)
  • 让它成为一个错误(正如@dave 在他的回答中所建议的那样)。如果有不止一个可行的选择,那就是编译器错误。 (这在静态语言中效果最好,你可以提前捕捉到这个东西。)
  • 只允许最左边的类定义运算符重载,就像在 C++ 中一样。 (那么你的 B 类就是非法的。)

  • 唯一的其他选择是为运算符重载引入一个复杂的优先系统,但是你说你想减少认知开销。


    评论关闭
    IT干货网

    微信公众号号:IT虾米 (左侧二维码扫一扫)欢迎添加!