是什么让Scala的运营商重载“好”,但是C ++的“糟糕”?

时间:2009-07-08 14:05:45

标签: c++ scala operator-overloading

许多人认为C ++中的运算符重载是A Bad Thing(tm),并且在新语言中不会重复错误。当然,这是设计Java时专门删除的一个功能。

现在我开始阅读Scala,我发现它看起来非常像运算符重载(虽然从技术上来说它没有运算符重载,因为它没有运算符,只有函数)。但是,它似乎与C ++中的运算符重载没有本质上的区别,我记得运算符被定义为特殊函数。

所以我的问题是什么使得在Scala中定义“+”的想法比在C ++中更好?

14 个答案:

答案 0 :(得分:238)

C ++从C继承了真正的蓝色运算符。我的意思是6 + 4中的“+”非常特殊。例如,您不能获得指向该+函数的指针。

另一方面,Scala没有这样的运营商。它在定义方法名称和非字符号的一些内置优先级方面具有很大的灵活性。因此技术上Scala没有运算符重载。

无论你想要什么,操作符重载本身并不坏,即使在C ++中也是如此。问题在于糟糕的程序员滥用它。但坦率地说,我认为剥夺程序员滥用运算符重载的能力并不能解决程序员可能滥用的所有问题。真正的答案是指导。 http://james-iry.blogspot.com/2009/03/operator-overloading-ad-absurdum.html

尽管如此,C ++的运算符重载和Scala灵活的方法命名之间存在差异,恕我直言,这使得Scala不那么容易滥用和更容易被滥用。

在C ++中,获取in-fix表示法的唯一方法是使用运算符。否则,您必须使用object.message(argument)或pointer-> messsage(参数)或函数(argument1,argument2)。因此,如果您希望代码具有某种DSLish风格,那么就有使用运算符的压力。

在Scala中,您可以通过任何消息发送获取中缀表示法。 “对象消息参数”完全没问题,这意味着您不需要使用非单词符号来获取中缀表示法。

C ++运算符重载仅限于C运算符。结合只有运营商可能会使用的限制,这会给人们带来压力,试图将各种不相关的概念映射到相对较少的符号,如“+”和“>>”

Scala允许大量有效的非单词符号作为方法名称。例如,我有一个嵌入式Prolog-ish DSL,你可以写

female('jane)!         // jane is female
parent('jane,'john)!   // jane is john's parent
parent('jane, 'wendy)! // jane is wendy's parent

mother('Mother, 'Child) :- parent('Mother, 'Child) & female('Mother) //'// a mother of a child is the child's parent and is female

mother('X, 'john)?  // find john's mother
mother('jane, 'X)?  // find's all of jane's children

: - ,!,?和&符号被定义为普通方法。仅限C ++和这将是有效的,因此尝试将此DSL映射到C ++将需要一些已经唤起非常不同的概念的符号。

当然,这也让Scala遭受了另一种虐待。在Scala中,如果您愿意,可以将方法命名为$!& ^%。

对于像Scala一样灵活使用非单词函数和方法名称的其他语言,请参阅Smalltalk,其中,与Scala一样,每个“运算符”只是另一种方法,而Haskell允许程序员定义优先级和固定性灵活命名的功能。

答案 1 :(得分:65)

  

C ++中的运算符重载是   被许多人认为是坏人   事(TM)

只有愚昧无知。在像C ++这样的语言中绝对需要它,并且值得注意的是,其他开始采用“纯粹主义”观点的语言,一旦他们的设计师发现它有多么必要,就添加了它。

答案 2 :(得分:41)

在C ++中,运算符重载从未被普遍认为是一个坏主意 - 只是滥用运算符重载被认为是一个坏主意。一个人不需要语言中的运算符重载,因为无论如何它们都可以使用更详细的函数调用进行模拟。避免Java中的运算符重载使得Java的实现和规范变得更加简单,并且它迫使程序员不要滥用运算符。 Java社区中存在一些关于引入运算符重载的争论。

Scala中运算符重载的优点和缺点与C ++中的相同 - 如果适当地使用运算符重载,则可以编写更自然的代码 - 如果不使用,则可以编写更加神秘,模糊的代码。

仅供参考:操作符未被定义为C ++中的特殊函数,它们的行为与任何其他函数一样 - 尽管名称查找存在一些差异,它们是否需要是成员函数,以及它们可以在两个函数中调用方法:1)运算符语法,以及2)operator-function-id语法。

答案 3 :(得分:18)

这篇文章 - “The Positive Legacy of C++ and Java” - 直接回答您的问题。

  

“C ++有堆栈分配和堆分配,你必须重载你的操作符来处理所有情况而不会导致内存泄漏。确实很困难。但是,Java有一个存储分配机制和一个垃圾收集器,这会使运营商超载琐碎的“......

Java错误地(根据作者)省略了运算符重载,因为它在C ++中很复杂,但是忘了为什么(或者没有意识到它不适用于Java)。

值得庆幸的是,像Scala这样的高级语言为开发人员提供了选项,同时仍在同一个JVM上运行。

答案 4 :(得分:9)

运算符重载没有错。事实上,有数字类型的运算符重载有问题。 (看一下使用BigInteger和BigDecimal的一些Java代码。)

但是,C ++有滥用这一功能的传统。一个经常被引用的例子是比特移位运算符被重载以进行I / O.

答案 5 :(得分:8)

总的来说,这不是一件坏事 C#等新语言也有运算符重载。

滥用运算符重载是一件坏事。

但是C ++中定义的运算符重载也存在问题。因为重载运算符只是方法调用的语法糖,所以它们的行为就像方法一样。另一方面,普通的内置运算符的行为与方法不同。这些不一致可能会导致问题。

关闭我的头顶操作员||&& 这些内置版本是快捷操作符。对于重载版本而言,情况并非如此,并且已导致一些问题。

+ - * / all返回与他们操作相同的类型(在操作员晋升后)的事实 重载版本可以返回任何内容(这是滥用设置的位置,如果您的操作员开始返回某些仲裁器类型,则用户不期望事情会下降)。

答案 6 :(得分:7)

运算符重载不是你真正“经常”需要的东西,但是当你使用Java时,如果你达到了真正需要它的程度,它会让你想要撕掉你的指甲,这样你就有了借口停止打字。

你刚发现的代码溢出了很长时间?是的,你将不得不重新输入整个批次以使其适用于BigInteger。没有什么比仅仅为了改变变量的类型而重新发明轮子更令人沮丧的了。

答案 7 :(得分:6)

Guy Steele认为运营商超载也应该是Java,在他的主题演讲“发展一种语言” - 有一个视频和它的转录,这真是一个惊人的演讲。你会想知道他在前几页会谈到什么,但如果你继续阅读,你会看到重点并获得启示。事实上他可以做这样的演讲也很棒。

与此同时,这次谈话激发了许多基础研究,可能包括Scala--这是每个人都应该阅读以在该领域工作的那些论文之一。

回到这一点,他的例子主要是关于数字类(比如BigInteger和一些更奇怪的东西),但这不是必需的。

但是,错误地使用运算符重载会导致可怕的结果,即使正确使用也会使问题复杂化,如果您尝试读取代码而不仔细研究它使用的库。但这是个好主意吗? OTOH,这些图书馆是否应该尝试为其运营商提供操作员备忘单?

答案 8 :(得分:4)

我相信每个答案都错过了这一点。在C ++中,您可以根据需要重载运算符,但不能影响它们的优先级。斯卡拉没有这个问题,IIRC。

至于这是一个坏主意,除了优先权问题之外,人们对运营商提出了非常愚蠢的意义,而且它很少有助于提高可读性。 Scala库对于这个愚蠢的符号特别不好,你每次都必须记住这些符号,图书馆维护人员坚持不懈地说:“你只需要学习一次”。好极了,现在我需要学习一些“聪明”作者的神秘语法*我关心的库的数量。如果存在总是提供有文化版本的运算符的惯例,那就不会那么糟糕了。

答案 9 :(得分:3)

运营商超载不是C ++的发明 - 它来自Algol IIRC,甚至Gosling也没有声称这是一个坏主意。

答案 10 :(得分:3)

C ++中唯一已知错误的是缺乏将[] =作为单独的运算符重载的能力。这可能很难在C ++编译器中实现,这可能不是一个明显的原因,但很有价值。

答案 11 :(得分:2)

正如其他答案所指出的那样;运算符重载本身并不一定是坏事。当它以使得结果代码不明显的方式使用时会有什么不好。一般来说,当你使用它们时,你需要让它们做最不令人惊讶的事情(有操作符+做分区会给理性类的使用带来麻烦)或者像Scott Meyers所说:

  

客户已经知道类型如何   int表现,所以你应该努力   让你的类型以相同的方式行事   什么时候合理...... 什么时候进去   怀疑,按照这样做做。   (来自Effective C ++ 3rd Edition第18项)

现在有些人已经通过boost::spirit之类的东西将运算符重载到极致。在这个级别你不知道它是如何实现的,但是它会产生一个有趣的语法来获得你想要的东西。我不确定这是好还是坏。看起来不错,但我还没用过它。

答案 12 :(得分:2)

我从未见过一篇声称C ++的运算符重载不好的文章。

用户可定义的运营商可以为语言用户提供更高水平的表达能力和可用性。

答案 13 :(得分:1)

  

但是,它似乎与C ++中的运算符重载没有本质上的区别,因为我记得运算符被定义为特殊函数。

AFAIK,与“普通”成员函数相比,运算符函数没有什么特别之处。当然,您只有一组可以重载的运算符,但这并不会使它们非常特殊。