Was macht Scalas Operator überladen "gut", aber C ++ "schlecht"?

155

Das Überladen von Operatoren in C ++ wird von vielen als A Bad Thing (tm) angesehen und als Fehler, der in neueren Sprachen nicht wiederholt werden darf. Sicherlich war es eine Funktion, die speziell beim Entwerfen von Java weggelassen wurde.

Nachdem ich angefangen habe, Scala zu lesen, stelle ich fest, dass es eine Art Operatorüberladung aufweist (obwohl es technisch gesehen keine Operatorüberladung gibt, weil es keine Operatoren, sondern nur Funktionen hat). Es scheint sich jedoch qualitativ nicht von der Operatorüberladung in C ++ zu unterscheiden, bei der Operatoren, wie ich mich erinnere, als Sonderfunktionen definiert sind.

Meine Frage ist also, warum die Idee, "+" in Scala zu definieren, eine bessere Idee ist als in C ++.

Skaffman
quelle
27
Weder C ++ noch Scala wurden von allen Programmierern durch allgemeinen Konsens definiert. Ich glaube nicht, dass es einen Widerspruch zwischen der Tatsache gibt, dass einige Leute sich für C ++ interessieren, und der Tatsache, dass sich einige Leute nicht für Scala interessieren.
Steve Jessop
16
Das Überladen von Operatoren in C ++ ist nicht schlecht.
Welpe
5
Dies ist nichts Neues, aber die Art und Weise, wie ich C ++ verteidige, wenn das Überladen von Operatoren und andere "erweiterte" Funktionen in Frage gestellt werden, ist einfach: C ++ gibt uns die Möglichkeit, es nach eigenem Ermessen zu verwenden / zu missbrauchen. Mir hat immer gefallen, wie wir als kompetent und autonom gelten und keine Entscheidungen wie diese für uns treffen müssen.
Elliott
Scala wurde wie Jahrzehnte nach c ++ entwickelt. Es stellt sich heraus, dass die Person dahinter in Bezug auf Programmiersprachen sehr versiert ist. An sich auch nichts Schlechtes, wenn man sich noch 100 Jahre an c ++ oder Scala hält, wird klar, dass wahrscheinlich beide schlecht sind! Voreingenommenheit liegt anscheinend in unserer Natur, aber wir können sie bekämpfen. Schauen Sie sich nur die Geschichte der Technologie an, alles wird obsolet.
Nader Ghanbari

Antworten:

242

C ++ erbt echte blaue Operatoren von C. Damit meine ich, dass das "+" in 6 + 4 etwas ganz Besonderes ist. Sie können zum Beispiel keinen Zeiger auf diese + Funktion bekommen.

Scala hingegen hat auf diese Weise keine Operatoren. Es hat nur eine große Flexibilität bei der Definition von Methodennamen und ein wenig Vorrang für Nicht-Wortsymbole. Technisch gesehen hat Scala also keine Bedienerüberlastung.

Wie auch immer Sie es nennen möchten, die Überladung von Operatoren ist selbst in C ++ nicht von Natur aus schlecht. Das Problem ist, wenn schlechte Programmierer es missbrauchen. Aber ehrlich gesagt bin ich der Meinung, dass das Wegnehmen der Fähigkeit von Programmierern, die Überlastung von Operatoren zu missbrauchen, nicht dazu führt, dass alle Dinge repariert werden, die Programmierer missbrauchen können. Die eigentliche Antwort ist Mentoring. http://james-iry.blogspot.com/2009/03/operator-overloading-ad-absurdum.html

Trotzdem gibt es Unterschiede zwischen der Überladung von C ++ - Operatoren und der flexiblen Methodenbenennung von Scala, die Scala meiner Meinung nach sowohl weniger missbräuchlich als auch missbräuchlicher machen.

In C ++ besteht die einzige Möglichkeit, eine In-Fix-Notation zu erhalten, in der Verwendung von Operatoren. Andernfalls müssen Sie object.message (Argument) oder pointer-> messsage (Argument) oder function (argument1, argument2) verwenden. Wenn Sie also einen bestimmten DSLish-Stil für Ihren Code wünschen, besteht der Druck, Operatoren zu verwenden.

In Scala können Sie bei jeder gesendeten Nachricht eine Infix-Notation erhalten. "Objektnachrichtenargument" ist vollkommen in Ordnung, was bedeutet, dass Sie keine Nicht-Wortsymbole verwenden müssen, um die Infixnotation zu erhalten.

Das Überladen von C ++ - Operatoren ist im Wesentlichen auf die C-Operatoren beschränkt. In Kombination mit der Einschränkung, dass nur Operatoren verwendet werden dürfen, wird ein Infix verwendet, das Druck auf die Benutzer ausübt, zu versuchen, eine Vielzahl von nicht verwandten Konzepten auf relativ wenige Symbole wie "+" und ">>" abzubilden.

Scala erlaubt eine Vielzahl gültiger Nicht-Wortsymbole als Methodennamen. Zum Beispiel habe ich ein eingebettetes Prolog-DSL, in das Sie schreiben können

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

Die Symbole: -,!,? Und & werden als normale Methoden definiert. Nur in C ++ & wäre gültig, sodass ein Versuch, dieses DSL in C ++ abzubilden, einige Symbole erfordern würde, die bereits sehr unterschiedliche Konzepte hervorrufen.

Dies öffnet Scala natürlich auch für eine andere Art von Missbrauch. In Scala können Sie eine Methode $! & ^% Nennen, wenn Sie möchten.

Für andere Sprachen, die wie Scala flexibel in der Verwendung von Nicht-Wort-Funktions- und Methodennamen sind, siehe Smalltalk, wo wie Scala jeder "Operator" nur eine andere Methode und Haskell ist, mit der der Programmierer Vorrang und Fixität von flexibel benannten Namen definieren kann Funktionen.

James Iry
quelle
Zuletzt habe ich überprüft, 3.operator + (5) hat funktioniert. Ich bin wirklich überrascht, dass & (3.operator +) dies nicht tut.
Joshua
Sie könnten zum Beispiel in c ++ behaupten (weiblich ("jane")). Das wäre überhaupt nicht verwirrend - nicken Sie zurück zu dem james-iry-Post darüber, dass Operator + keine schlechte Sache ist, aber dumme Programmierer.
pm100
1
@ Joshua int main() {return (3).operator+(5);}Ergebnisse inerror: request for member ‘operator+’ in ‘3’, which is of non-class type ‘int’
zildjohn01
Das ist ein Haufen arroganter Scheiße: "Das Überladen von Operatoren ist selbst in C ++ nicht von Natur aus schlecht. Das Problem ist, wenn schlechte Programmierer es missbrauchen." Wenn etwas leicht zu missbrauchen ist und nur wenig davon profitiert, führt dies insgesamt dazu, dass der nächste Benutzer, der Ihren Code verwaltet, die Produktivität beim Entschlüsseln der seltsameren Teile Ihres Codes verliert. Ansonsten: Sehr informative und gut geschriebene Antwort.
Jukka Dahlbom
@JukkaDahlbom Das Vorhandensein intelligenter Zeiger macht den Nutzen für sich genommen groß. Und dann haben Sie Lambdas, benutzerdefinierte Nummerntypen, Intervalltypen ...
Alexey Romanov
66

Das Überladen von Operatoren in C ++ wird von vielen als A Bad Thing (tm) angesehen.

Nur von den Unwissenden. In einer Sprache wie C ++ ist dies unbedingt erforderlich, und es fällt auf, dass andere Sprachen, die zunächst eine "puristische" Sichtweise vertraten, diese hinzugefügt haben, sobald ihre Designer herausgefunden haben, wie notwendig dies ist.


quelle
30
Ich stimme Neil tatsächlich zu. Das Überladen von Operatoren ist wichtig, wenn Sie Variablen / Konstanten / Objekte / Instanzen als algebraische Entitäten darstellen möchten ... und die Benutzer ihre Interaktionen auf mathematische Weise verstehen möchten - so sollte die Programmierung meiner Meinung nach funktionieren.
Massa
16
+1, Operatorüberladung in C ++ ist gut. Zum Beispiel macht es Vektormathematik viel sauberer. Wie bei vielen C ++ - Funktionen sollten Sie die Leistung sorgfältig einsetzen.
John Smith
7
@Kristo Da C ++ Werte verwendet, die zugewiesen und kopiert werden müssen. Es ist erforderlich, die Kontrolle darüber zu haben, damit Sie mindestens den Zuweisungsoperator für einen bestimmten Typ angeben können können.
7
@Kristo: weil eine Absicht von C ++ darin besteht, benutzerdefinierten Typen zu erlauben, alles zu tun, was eingebaute Typen tun (obwohl sie in einigen Kontexten wie impliziten Konvertierungen unterschiedlich behandelt werden). Wenn Sie eine 27-Bit-Ganzzahl implementieren möchten, können Sie dies, und die Verwendung entspricht der Verwendung von int. Ohne Operatorüberladung wäre es nicht möglich, UDTs mit derselben Syntax wie eingebaute Typen zu verwenden, und daher wäre die resultierende Sprache in diesem Sinne nicht "wie C ++".
Steve Jessop
8
"so liegt der Wahnsinn" - noch schlimmer, so liegt std :: vector <bool>!
Steve Jessop
42

Das Überladen von Operatoren wurde in C ++ nie allgemein als schlechte Idee angesehen - nur der Missbrauch des Überladens von Operatoren wurde als schlechte Idee angesehen. Das Überladen von Operatoren in einer Sprache ist nicht wirklich erforderlich, da sie ohnehin mit ausführlicheren Funktionsaufrufen simuliert werden können. Das Vermeiden einer Überladung von Operatoren in Java vereinfachte die Implementierung und Spezifikation von Java ein wenig und zwang Programmierer, Operatoren nicht zu missbrauchen. In der Java-Community gab es einige Debatten über die Einführung einer Überladung von Operatoren.

Die Vor- und Nachteile der Operatorüberladung in Scala sind dieselben wie in C ++ - Sie können natürlicheren Code schreiben, wenn Sie die Operatorüberladung entsprechend verwenden - und kryptischeren, verschleierten Code, wenn Sie dies nicht tun.

Zu Ihrer Information: Operatoren sind in C ++ nicht als Sonderfunktionen definiert, sie verhalten sich wie jede andere Funktion - obwohl es einige Unterschiede bei der Namenssuche gibt, ob sie Mitgliedsfunktionen sein müssen und die Tatsache, dass sie auf zwei Arten aufgerufen werden können: 1 ) Operatorsyntax und 2) Operatorfunktions-ID-Syntax.

Faisal Vali
quelle
"Man braucht keine Operatorüberladung in einer Sprache, da sie sowieso mit ausführlicheren Funktionsaufrufen simuliert werden können." Nach dieser Logik braucht man nicht einmal wirklich Operatoren . Warum nicht einfach benutzen add(2, multiply(5, 3))?
Joe Z.
Es geht eher darum, die üblichen verwendeten Notationen abzugleichen. Betrachten Sie Mathematiker und Physiker, sie können eine C ++ - Bibliothek verstehen und verwenden, die das Überladen der Operatoren viel einfacher macht. Sie konzentrieren sich lieber auf die Gleichung als auf die Programmiersprache.
Phil Wright
19

Dieser Artikel - " Das positive Erbe von C ++ und Java " - beantwortet Ihre Frage direkt.

"C ++ verfügt sowohl über eine Stapelzuweisung als auch über eine Heapzuweisung, und Sie müssen Ihre Operatoren überlasten, um alle Situationen zu bewältigen und keine Speicherlecks zu verursachen. In der Tat schwierig. Java verfügt jedoch über einen einzigen Speicherzuweisungsmechanismus und einen Garbage Collector, wodurch das Überladen von Operatoren trivial wird." ..

Java hat fälschlicherweise (laut Autor) das Überladen von Operatoren weggelassen, da es in C ++ kompliziert war, aber vergessen, warum (oder nicht erkannt hat, dass es nicht für Java gilt).

Glücklicherweise bieten höhere Sprachen wie Scala Entwicklern Optionen, während sie immer noch auf derselben JVM ausgeführt werden.

jmanning2k
quelle
14
Eckel ist die einzige Quelle, die ich jemals für die Idee gesehen habe, dass die Überladung von Operatoren aufgrund von Komplikationen in C ++ aus Java entfernt wurde, und er sagt nicht, was seine Quelle ist. Ich würde es rabattieren. Alle anderen Quellen, die ich habe, sagen, dass es wegen möglichen Missbrauchs fallen gelassen wurde. Siehe gotw.ca/publications/c_family_interview.htm und newt.com/wohler/articles/james-gosling-ramblings-1.html . Suchen Sie sie einfach auf der Seite nach "Operatorüberladung".
James Iry
9

An der Überlastung des Bedieners ist nichts auszusetzen. Tatsächlich stimmt etwas nicht, wenn der Operator für numerische Typen nicht überladen wird. (Sehen Sie sich Java-Code an, der BigInteger und BigDecimal verwendet.)

C ++ hat jedoch die Tradition, die Funktion zu missbrauchen. Ein häufig genanntes Beispiel ist, dass die Bitshift-Operatoren für E / A überlastet sind.

dan04
quelle
<< und >> Betreiber visuell sind angibt Wege der Übertragung werden sie gemeint tun I / O, es ist nicht zu missbrauchen, ist es von Standard - Bibliothek und praktische Sache. Schauen Sie sich einfach "cin >> etwas" an, was geht wohin? Offensichtlich von cin zu etwas.
Peenut
7
@peenut: Aber ihre ursprüngliche Verwendung war bitverschiebend. Die "Standardbibliothek" verwendet den Operator so, dass die ursprüngliche Definition vollständig beeinträchtigt wird.
Joe Z.
1
Ich bin sicher, ich habe irgendwo gelesen, dass Bjarne Stroustrup (Schöpfer von C ++) mit und =anstelle von <<und >>in den frühen Tagen von C ++ experimentiert hat , aber auf Probleme gestoßen ist, da es nicht die richtige Operator-Priorität hatte (dh es sucht Argumente links oder rechts zuerst). Also waren seine Hände ein bisschen gebunden, was er gebrauchen konnte.
Phil Wright
8

Im Allgemeinen ist es keine schlechte Sache.
Neue Sprachen wie C # haben auch eine Überladung des Operators.

Es ist der Missbrauch der Überlastung des Bedieners, der eine schlechte Sache ist.

Es gibt jedoch auch Probleme mit der in C ++ definierten Überladung von Operatoren. Da überladene Operatoren nur syntaktischer Zucker für Methodenaufrufe sind, verhalten sie sich wie Methoden. Andererseits verhalten sich normale integrierte Operatoren nicht wie Methoden. Diese Inkonsistenz kann Probleme verursachen.

Aus dem Kopf Operatoren ||und &&.
Die eingebauten Versionen davon sind Verknüpfungsoperatoren. Dies gilt nicht für überladene Versionen und hat einige Probleme verursacht.

Die Tatsache, dass + - * / alle denselben Typ zurückgeben, mit dem sie arbeiten (nach der Heraufstufung des Betreibers).
Die überladenen Versionen können alles zurückgeben. (Hier setzt der Missbrauch ein. Wenn Ihre Bediener beginnen, einen vom Benutzer nicht erwarteten Schiedsrichtertyp zurückzugeben Dinge gehen bergab).

Martin York
quelle
8

Das Überladen von Operatoren ist nicht etwas, das Sie wirklich sehr oft "brauchen", aber wenn Sie Java verwenden und einen Punkt erreichen, an dem Sie es wirklich brauchen, werden Sie Lust haben, Ihre Fingernägel herauszureißen, nur damit Sie eine Ausrede haben, mit dem Tippen aufzuhören .

Der Code, den Sie gerade gefunden haben, läuft lange über? Ja, Sie müssen das gesamte Los erneut eingeben, damit es mit BigInteger funktioniert. Es gibt nichts Frustrierenderes, als das Rad neu erfinden zu müssen, um den Typ einer Variablen zu ändern.


quelle
6

Guy Steele argumentierte in seiner Grundsatzrede "Eine Sprache wachsen lassen", dass das Überladen von Operatoren auch in Java erfolgen sollte - es gibt ein Video und eine Transkription davon, und es ist wirklich eine erstaunliche Rede. Sie werden sich fragen, wovon er auf den ersten Seiten spricht, aber wenn Sie weiterlesen, werden Sie den Punkt erkennen und Erleuchtung erreichen. Und die Tatsache, dass er überhaupt eine solche Rede halten konnte, ist auch erstaunlich.

Gleichzeitig inspirierte dieser Vortrag viele Grundlagenforschungen, wahrscheinlich auch Scala - es ist eines dieser Papiere, die jeder lesen sollte, um vor Ort zu arbeiten.

Zurück zum Punkt, in seinen Beispielen geht es hauptsächlich um numerische Klassen (wie BigInteger und einige seltsamere Dinge), aber das ist nicht wesentlich.

Es ist jedoch richtig, dass ein Missbrauch der Überladung von Operatoren zu schrecklichen Ergebnissen führen kann und dass selbst eine ordnungsgemäße Verwendung die Sache komplizieren kann, wenn Sie versuchen, Code zu lesen, ohne die verwendeten Bibliotheken ein wenig zu studieren. Aber ist das eine gute Idee? OTOH, sollten solche Bibliotheken nicht versuchen, ein Operator-Spickzettel für ihre Operatoren aufzunehmen?

Blaisorblade
quelle
4

Ich glaube, JEDE Antwort hat dies verpasst. In C ++ können Sie Operatoren überladen, was Sie möchten, aber Sie können die Priorität, mit der sie ausgewertet werden, nicht beeinflussen. Scala hat dieses Problem nicht, IIRC.

Da es sich um eine schlechte Idee handelt, haben die Leute neben Prioritätsproblemen auch wirklich dumme Bedeutungen für die Bediener, und dies hilft selten bei der Lesbarkeit. Scala-Bibliotheken sind besonders schlecht für diese albernen Symbole, die Sie sich jedes Mal merken müssen. Bibliotheksverwalter stecken ihre Köpfe in den Sand und sagen: "Sie müssen es nur einmal lernen." Großartig, jetzt muss ich die kryptische Syntax eines 'cleveren' Autors * lernen, wie viele Bibliotheken ich verwenden möchte. Es wäre nicht so schlimm, wenn es eine Konvention gäbe, die IMMER eine literarische Version der Operatoren liefert.

Saem
quelle
1
Scala hat auch eine feste Operator-Priorität, nicht wahr?
Skaffman
Ich glaube schon, aber es ist viel flacher. Scala hat weniger Betreiberzeit. +, -, * sind Methoden, keine Operatoren, IIRC. Deshalb ist 2 + 3 * 2 nicht 8, sondern 10.
Saem
7
Scala hat ein Prioritätssystem, das auf dem ersten Zeichen des Symbols basiert. scala> 2 + 3 * 2 res0: Int = 8
James Iry
3

Das Überladen von Operatoren war keine C ++ - Erfindung - es stammte von Algol IIRC und selbst Gosling behauptet nicht, dass es im Allgemeinen eine schlechte Idee ist.

Nemanja Trifunovic
quelle
Sicher, aber es war in seiner C ++ - Inkarnation, dass es eine allgemeine Atmosphäre der Disreputabilität gewann.
Skaffman
5
Was meinst du mit "allgemeiner Luft der Verrufbarkeit"? Die meisten Leute, die ich kenne, verwenden Sprachen, die das Überladen von Operatoren unterstützen (C ++, C #), und ich habe noch nie Beschwerden gehört.
Nemanja Trifunovic
Ich spreche aus meiner langjährigen Erfahrung mit Pre-ANSI C ++ und erinnere mich sicherlich an eine häufige Abneigung gegen sie. Vielleicht hat sich die Situation mit ANSI C ++ verbessert, oder die Leute haben gerade gelernt, wie man es nicht missbraucht.
Skaffman
1
Als jemand, der C ++ seit Tagen (Mitte der 80er Jahre) verwendet, kann ich Ihnen versichern, dass die Einführung des ISO-Standards keine Auswirkungen auf die Vorurteile der Menschen hinsichtlich der Überlastung von Bedienern hatte.
3

Das einzige, was in C ++ als falsch bekannt ist, ist das Fehlen der Fähigkeit, [] = als separaten Operator zu überladen. Dies könnte schwierig in einem C ++ - Compiler zu implementieren sein, was wahrscheinlich kein offensichtlicher Grund ist, aber es lohnt sich.

Joshua
quelle
2

Wie die anderen Antworten gezeigt haben; Die Überlastung des Bedieners selbst ist nicht unbedingt schlecht. Was ist schlecht daran, wenn es so verwendet wird, dass der resultierende Code nicht mehr offensichtlich ist? Wenn Sie sie verwenden, müssen Sie sie im Allgemeinen dazu bringen, die am wenigsten überraschende Sache zu tun (Operator + Do-Division würde Probleme für die Verwendung einer rationalen Klasse verursachen) oder wie Scott Meyers sagt:

Clients wissen bereits, wie sich Typen wie int verhalten. Daher sollten Sie sich bemühen, dass sich Ihre Typen immer dann gleich verhalten, wenn dies zumutbar ist. Wenn Sie Zweifel haben, tun Sie dies wie die Ints . (Aus Effective C ++ 3rd Edition, Punkt 18)

Jetzt haben einige Leute die Überlastung des Bedieners mit Dingen wie boost :: spirit auf die Spitze getrieben . Auf dieser Ebene haben Sie keine Ahnung, wie es implementiert ist, aber es macht eine interessante Syntax, um das zu erreichen, was Sie wollen. Ich bin mir nicht sicher, ob das gut oder schlecht ist. Es scheint schön, aber ich habe es nicht benutzt.

Matt Price
quelle
Ich argumentiere hier nicht für oder gegen eine Überlastung der Bediener, ich suche keine Leute, die sie rechtfertigen.
Skaffman
Sprint kommt dem schlechtesten Beispiel, auf das ich gestoßen bin, nicht nahe - Sie sollten sehen, was die RogueWave-Datenbankbibliothek macht!
Ich bin damit einverstanden, dass Spirit die Operatoren missbraucht, aber ich kann mir keinen besseren Weg vorstellen, dies zu tun.
Zifre
1
Ich denke nicht, dass Spirit die Betreiber ziemlich missbraucht, aber er treibt es voran. Ich bin damit einverstanden, dass es wirklich keinen anderen Weg gibt. Grundsätzlich wird ein DSL innerhalb der C ++ - Syntax erstellt. Sehr weit entfernt von dem, wofür C ++ entwickelt wurde. Ja, es gibt weitaus schlechtere Beispiele :) Im Allgemeinen verwende ich sie gegebenenfalls. Meist nur die Streaming-Operatoren zum einfacheren Debuggen \ Protokollieren. Und selbst dort leitet nur Zucker an eine in der Klasse implementierte Methode weiter.
Matt Price
1
Es ist eine Geschmacksfrage; Aber Parser-Kombinator-Bibliotheken in funktionalen Sprachen überladen Operatoren auf eine Weise , die Spirit sehr ähnlich ist , und niemand spricht sich dagegen aus. Es gibt viele technische Gründe, aus denen sie besser sind - Google für "Embedded Domain-spezifische Sprachen", um zahlreiche Artikel zu finden, die dies aus allgemeiner Sicht erklären, und Google für "Scala Parser Combinator" für praktische Beispiele in diesem Fall. Es ist wahr, dass in funktionalen Sprachen die resultierende Syntax oft besser ist - zum Beispiel müssen Sie die Bedeutung von >> für die Verkettung von Parsern nicht ändern.
Blaisorblade
2

Ich habe noch nie einen Artikel gesehen, in dem behauptet wird, dass die Überladung von C ++ - Operatoren schlecht ist.

Benutzerdefinierte Operatoren ermöglichen Benutzern der Sprache ein einfacheres Maß an Ausdruckskraft und Benutzerfreundlichkeit.

Paul Nathan
quelle
1

Es scheint sich jedoch qualitativ nicht von der Operatorüberladung in C ++ zu unterscheiden, bei der Operatoren, wie ich mich erinnere, als Sonderfunktionen definiert sind.

AFAIK, Bedienerfunktionen haben nichts Besonderes im Vergleich zu "normalen" Elementfunktionen. Natürlich haben Sie nur eine Reihe von Operatoren, die Sie überladen können, aber das macht sie nicht besonders.

John Smith
quelle