Was sind die Probleme, C ++ - like const in eine Sprache zu bringen?

17

Ich interessiere mich für die Idee von C ++ - wie constnicht diese bestimmte Ausführung (wie das Wegwerfen const).

Nehmen Sie zum Beispiel C # - es fehlt C ++ - wie const, und der Grund dafür ist das übliche - Menschen und Zeit. Hier scheint es, dass das C # -Team sich die C ++ - Ausführung von constMarketing-CLR angeschaut hat und an diesem Punkt genug hatte (siehe, warum es keine const-Member-Methode in c # und const -Parametern gibt ; danke, Svick). Seien Sie, wenn wir uns weiter bewegen, ist etwas anderes?

Gibt es etwas tieferes? Nehmen wir zum Beispiel die Mehrfachvererbung - sie wird normalerweise als schwer zu fassen angesehen (für den Benutzer) und daher nicht zur Sprache hinzugefügt (wie das Diamantproblem).

Gibt es etwas in NATURE dem , constdass ein Problem dar , dass es besser ist für die Sprache , es zu vermeiden? So etwas wie die Entscheidung, ob constes tief oder flach sein soll (ein constContainer bedeutet, dass ich kein neues Element hinzufügen oder vorhandene Elemente ändern kann; was ist, wenn Elemente vom Referenztyp sind)?

UPDATE : Während ich C # erwähne und damit eine historische Perspektive mache, interessiere ich mich für die Art der möglichen Probleme constder Sprache.

Dies ist keine Herausforderung für die Sprachen. Bitte ignorieren Sie solche Faktoren wie aktuelle Trends oder Popularität - ich interessiere mich nur für technische Fragen - danke.

Greenoldman
quelle
3
Nebenbei bemerkt: Mehrfachvererbung ist vielleicht nicht schwer zu verstehen, aber die Methodenauflösung kann ziemlich hässlich werden. Naive Tiefenauflösung wird schnell uninteressant („ Diamantproblem “). Die Auflösungsreihenfolge der C3-Methode (veröffentlicht '96) löst dies, ist aber sicherlich nicht leicht zu verstehen. Das Verbot der Mehrfachvererbung kann daher oftmals durchaus sinnvoll erscheinen - das eigentliche Problem ist jedoch, dass Java vor dem C3-Algorithmus liegt und sich C # an Java orientiert.
amon
3
@amon Viele Leute denken nicht wirklich, dass Mehrfachvererbung eine Funktion ist, die Sie in erster Linie haben möchten, z. B. die Entwickler der D-Programmiersprache: Es ist eine komplexe Funktion von umstrittenem Wert. Es ist sehr schwierig, es effizient zu implementieren, und Compiler sind anfällig für viele Fehler bei der Implementierung. Nahezu der gesamte Wert von MI kann mit einfacher Vererbung in Verbindung mit Schnittstellen und Aggregation verarbeitet werden. Was übrig bleibt, rechtfertigt nicht das Gewicht der MI-Implementierung. (Quelle)
jcora
5
Duplikat von stackoverflow.com/q/4150478/41071 .
Svick
2
Auch ohne Mehrfachvererbung, können Sie bereits kodieren jedes 3-SAT Problem als Methode Auflösung (oder genaue Überladungsauflösung) in C #, so dass es NP-vollständig zu machen.
Jörg W Mittag
1
@svick, vielen Dank. Da ich keine Antworten auf diese Frage hatte, habe ich mir erlaubt, sie gründlich zu bearbeiten, um mich auf die Art des Problems zu konzentrieren, nicht auf die historischen Gründe, da 99% davon "Zeit + Leute + Anti-C ++ - Voreingenommenheit" zu sein scheinen + CLR ".
Grünling

Antworten:

10

Beachten Sie, ob dies für Sie geeignet ist, aber in funktionalen Sprachen wie Standard ML ist standardmäßig alles unveränderlich. Die Mutation wird durch einen generischen refErence-Typ unterstützt. Eine intVariable ist also unveränderlich, und eine ref intVariable ist ein veränderlicher Container für ints. Grundsätzlich sind Variablen echte Variablen im mathematischen Sinne (ein unbekannter, aber fester Wert) und refs sind "Variablen" im imperativen Programmiersinn - eine Speicherzelle, in die geschrieben und aus der gelesen werden kann. (Ich nenne sie gerne zuweisbare Werte .)

Ich denke, das Problem constist zweifach. Erstens fehlt C ++ die Garbage Collection, die für nicht triviale persistente Datenstrukturen erforderlich ist . const muss tief sein, um einen Sinn zu ergeben, aber es ist unpraktisch, vollständig unveränderliche Werte in C ++ zu haben.

Zweitens müssen Sie sich in C ++ anmelden const anstatt sich dagegen zu entscheiden. Wenn Sie jedoch constetwas vergessen und später korrigieren, werden Sie in die in der Antwort von @ RobY erwähnte "const poisoning" -Situation geraten, in der die constÄnderung im gesamten Code kaskadiert. Wenn dies constder Standard wäre, würden Sie sich nicht constrückwirkend bewerben . Außerdem führt das Hinzufügen von constüberall zu viel Rauschen im Code.

Ich vermute, dass die folgenden Mainstream-Sprachen (z. B. Java) stark vom Erfolg und der Denkweise von C und C ++ geprägt waren. Beispiel: Selbst bei der Garbage Collection gehen die meisten Collection-APIs von veränderlichen Datenstrukturen aus. Die Tatsache, dass alles veränderlich und unveränderlich ist, wird als Eckpfeiler angesehen und spricht Bände über die zwingende Denkweise hinter populären Sprachen.

EDIT : Nachdem ich über den Kommentar von greenoldman nachgedacht hatte, wurde mir klar, dass constes nicht direkt um die Unveränderlichkeit von Daten geht. constCodiert in den Typ der Methode, ob sie Nebenwirkungen auf die Instanz hat.

Mutationen können verwendet werden, um ein referenziell transparentes Verhalten zu erzielen . Angenommen, Sie haben eine Funktion, die beim sukzessiven Aufruf unterschiedliche Werte zurückgibt - zum Beispiel eine Funktion, aus der ein einzelnes Zeichen gelesen wird stdin. Wir könnten die Ergebnisse dieser Funktion zwischenspeichern / auswendig lernen, um einen referenziell transparenten Strom von Werten zu erzeugen. Der Stream ist eine verknüpfte Liste, deren Knoten die Funktion aufrufen, wenn Sie zum ersten Mal versuchen, ihren Wert abzurufen, dann aber das Ergebnis zwischenspeichern. Also wennstdin constains ist Hello, world!, wird beim ersten Versuch, den Wert des ersten Knotens abzurufen, einer gelesen charund zurückgegeben H. Danach kehrt es Hohne weitere Aufrufe weiter zum Lesen ein char. Ebenso würde der zweite Knoten einen Fest charausstdinWenn Sie zum ersten Mal versuchen, seinen Wert abzurufen, wird dieses eErgebnis zurückgegeben und zwischengespeichert.

Das Interessante dabei ist, dass Sie einen Prozess, der von Natur aus zustandsbehaftet ist, in ein Objekt verwandelt haben, das scheinbar zustandslos ist. Es war jedoch notwendig, den internen Zustand des Objekts zu mutieren (indem die Ergebnisse zwischengespeichert wurden), um dies zu erreichen - die Mutation war ein harmloser Effekt . Es ist unmöglich, unseren zu CharStream consterzeugen, obwohl sich der Stream wie ein unveränderlicher Wert verhält . Stellen Sie sich vor, es gibt eine StreamSchnittstelle mit constMethoden und alle Funktionen, die Sie erwarten const Streams. Sie CharStreamkönnen die Schnittstelle nicht implementieren!

( EDIT 2: Anscheinend gibt es ein C ++ - Schlüsselwort mutable, mit dem wir betrügen und machen könnenCharStream const . Jedoch zerstört diese Lücke die constGarantien - jetzt können Sie wirklich nicht sicher sein, dass etwas durch seine constMethoden nicht mutiert . Ich nehme an, dass es nicht so ist schlecht, da Sie die Lücke explizit beantragen müssen, aber immer noch voll und ganz auf das Ehrensystem angewiesen sind.)

Angenommen, Sie haben Funktionen höherer Ordnung, dh Sie können Funktionen als Argumente an andere Funktionen übergeben. constness ist Teil der Signatur einer Funktion, sodass Sie keineconst als Argumente an Funktionen übergeben, die constFunktionen erwarten . Eine blinde Durchsetzung constwürde zu einem Verlust der Allgemeinheit führen.

Schließlich manipulieren a const garantiert das Objekts nicht, dass es keinen externen (statischen oder globalen) Zustand hinter Ihrem Rücken mutiert, sodass constdie Garantien nicht so stark sind, wie sie anfänglich angezeigt werden.

Mir ist nicht klar, dass es allgemein gut ist, das Vorhandensein oder Nichtvorhandensein von Nebenwirkungen in das Typsystem zu kodieren.

Doval
quelle
1
+1 Danke. Unveränderliche Objekte sind etwas anderes als C ++ const (es ist eher eine Ansicht). Aber sagen wir, C ++ hätte standardmäßig const und würde varbei Bedarf nur ein Problem hinterlassen - die Tiefe?
Greenoldman
1
@ Greenoldman Guter Punkt. Es ist schon eine Weile her, dass ich C ++ verwendet habe, also habe ich vergessen, dass Sie einen constVerweis auf ein Nicht- constObjekt haben können. Trotzdem glaube ich nicht, dass es Sinn macht, eine flache zu haben const. Der springende Punkt constist die Garantie, dass Sie das Objekt über diese Referenz nicht ändern können. Sie dürfen nicht umgehen, constindem Sie eine Nichtreferenz erstellen. constWarum sollte es also in Ordnung sein, zu umgehen, constindem Sie die Mitglieder des Objekts mutieren?
Doval
+1 :-) Sehr interessante Probleme in Ihrem Update (leben mit non / const Lambdas und vielleicht schwächeren Problem mit externen Zustand), muss ich es länger verdauen. Was Ihren Kommentar betrifft, habe ich nichts Böses im Sinn - ganz im Gegenteil, ich suche nach Funktionen, um die Klarheit des Codes zu erhöhen (ein weiterer: Nicht-Null-Zeiger). Gleiche Kategorie hier (zumindest ist es mein Zweck) - Ich definiere func foo(const String &s)und dies ist ein Signal, sin dem nichts geändert wird foo.
Greenoldman
1
@greenoldman Ich kann definitiv die Anziehungskraft und Begründung sehen. Ich glaube jedoch nicht, dass es eine wirkliche Lösung für das Problem gibt, außer den veränderlichen Zustand so weit wie möglich zu vermeiden (zusammen mit anderen bösen Dingen wie nullund Vererbung).
Doval
8

Das Hauptproblem ist, dass Programmierer dazu neigen, es nicht ausreichend zu verwenden. Wenn sie also an eine Stelle gelangen, an der es erforderlich ist, oder wenn ein Betreuer später die Konstanten-Korrektheit korrigieren möchte, gibt es einen enormen Welligkeitseffekt. Sie können immer noch einwandfreien unveränderlichen Code schreiben, ohne den constIhr Compiler nicht erzwingen wird, und es wird schwieriger sein, ihn zu optimieren. Einige Leute bevorzugen, dass ihr Compiler ihnen nicht hilft. Ich verstehe diese Leute nicht, aber es gibt viele von ihnen.

In funktionalen Sprachen ist so ziemlich alles möglich const, und Veränderlichkeit ist die seltene Ausnahme, wenn sie überhaupt zulässig ist. Dies hat mehrere Vorteile, wie zum Beispiel eine einfachere Parallelität und eine einfachere Überlegung des gemeinsam genutzten Zustands. Es ist jedoch gewöhnungsbedürftig, wenn Sie aus einer Sprache mit einer veränderlichen Kultur stammen. Mit anderen Worten, nicht zu wollen constist eine Frage der Menschen, keine technische.

Karl Bielefeldt
quelle
+1 Danke. Während unveränderliches Objekt etwas anderes ist, als ich suche (Sie können Objekt in C # oder Java unveränderlich machen), müssen Sie selbst bei unveränderlichem Ansatz entscheiden, ob es tief oder flach ist, zum Beispiel den Container mit Zeigern / Referenzen (können Sie) Ändern Sie den Wert von Objekten, auf die gezeigt wird. Nach einem Tag scheint das Hauptproblem eher das zu sein, was als Standard festgelegt ist, und es ist leicht lösbar, wenn eine neue Sprache entworfen wird.
Greenoldman
1
+1 für die lolz @ "Einige Leute bevorzugen, dass ihr Compiler ihnen nicht hilft. Ich verstehe diese Leute nicht, aber es gibt viele von ihnen."
Thomas Eding
6

Ich sehe die Nachteile als:

  • "const-Vergiftung" ist ein Problem, wenn eine Deklaration von const eine vorherige oder folgende Deklaration zwingen kann, const zu verwenden, und dies kann dazu führen, dass die vorherige oder folgende Deklaration const verwendet, usw. Und wenn die const-Vergiftung in eine Klasse in einer Bibliothek fließt dass Sie nicht die Kontrolle haben, dann können Sie an einem schlechten Ort stecken.

  • Es ist keine absolute Garantie. Es gibt normalerweise Fälle, in denen die Konstante nur die Referenz schützt, die zugrunde liegenden Werte jedoch aus dem einen oder anderen Grund nicht schützen kann. Sogar in C gibt es Randfälle, an die const nicht herankommen kann, was das Versprechen, das const bietet, schwächt.

  • Im Allgemeinen scheint sich die Stimmung in der Branche von starken Kompilierungszeitgarantien zu entfernen, wie viel Komfort sie Ihnen und mir auch bringen mögen. Deshalb verbringe ich den Nachmittag damit, 66% meiner Codebasis zu berühren, weil ich eine Konstantenvergiftung habe, etwas Python guy hat 10 perfekt funktionierende, gut gestaltete, erprobte und voll funktionsfähige Python-Module herausgeholt. Und er hackte nicht einmal, als er es tat.

Vielleicht stellt sich die Frage, warum ein potenziell umstrittenes Feature, das selbst einige Experten als ambivalent bezeichnen, wenn Sie versuchen, Ihre schicke neue Sprache einzuführen.

EDIT: kurze Antwort, eine neue Sprache hat einen steilen Hügel zur Adoption zu erklimmen. Die Designer müssen sich wirklich genau überlegen, welche Funktionen sie benötigen, um sich durchzusetzen. Nehmen Sie zum Beispiel Go. Google hat einige der tiefsten religiösen Schlachten brutal abgeschnitten, indem es einige Entwürfe im Conan-the-Barbarian-Stil ausgewählt hat, und ich denke tatsächlich, dass die Sprache besser dafür ist, nach dem, was ich gesehen habe.

rauben
quelle
2
Deine zweite Kugel ist nicht korrekt. In C ++ gibt es drei / vier Möglichkeiten, einen Verweis / Zeiger wrt zu deklarieren. Konstantheit: int *wandelbar Zeiger auf veränderbaren Wert, int const *änderbare Zeiger auf const Wert, int * constconst Zeiger auf veränderbaren Wert, int const * constconst Zeiger auf const Wert.
Phresnel
Vielen Dank. Ich habe nach der Natur gefragt, nicht nach dem Marketing ("Industrie wegziehen" ist nicht die Natur von C ++ const), daher halte ich solche "Gründe" für irrelevant (zumindest für diese Frage). Über constVergiftungen - können Sie ein Beispiel geben? Weil AFAIK es der Vorteil ist, übergibt man etwas constund es soll bleiben const.
Greenoldman
1
Es ist nicht das constProblem, aber die Art wirklich zu ändern - was auch immer Sie tun, es wird immer ein Problem sein. Überlegen Sie RichString, ob Sie passen möchten , und stellen Sie dann fest, dass Sie besser passen String.
Greenoldman
4
@RobY: Ich bin nicht sicher, an wen Sie sich mit letzteren und früheren wenden . Allerdings: Nach diesem Nachmittag, in dem Sie den Code durchgesehen haben, sollten Sie gelernt haben, dass Sie die Konstanz von unten nach oben und nicht von oben nach unten korrigieren sollten, dh in den innersten Einheiten beginnen und sich nach oben arbeiten sollten. Vielleicht waren Ihre Einheiten auch nicht isoliert genug, sondern eng miteinander verbunden, oder Sie sind Opfer innerer Systeme geworden.
Phresnel
7
"Const-Vergiftung" ist eigentlich kein Problem - das eigentliche Problem ist, dass C ++ standardmäßig auf non- eingestellt ist const. Es ist zu einfach, constDinge, die sein sollten, nicht constzu tun , weil man sich dafür entscheiden muss, anstatt sich dagegen zu entscheiden.
Doval
0

Es gibt einige philosophische Probleme beim Hinzufügen constzu einer Sprache. Wenn Sie consteine Klasse deklarieren , bedeutet dies, dass sich die Klasse nicht ändern kann. Eine Klasse ist jedoch eine Reihe von Variablen, die mit Methoden (oder Mutatoren) gekoppelt sind. Wir erreichen die Abstraktion, indem wir den Zustand einer Klasse hinter einer Reihe von Methoden verbergen. Wenn eine Klasse so konzipiert ist, dass sie den Status verbirgt, benötigen Sie Mutatoren, um diesen Status von außen zu ändern, und dann ist er nicht constmehr vorhanden. Es ist also nur möglich, constKlassen zu deklarieren , die unveränderlich sind. So constscheint nur in Szenarien , in denen die Klassen (oder die Typen , die Sie erklären mögen const) trivial sind ...

Also brauchen wir constsowieso? Ich glaube nicht. Ich denke, dass Sie leicht verzichten können, constwenn Sie ein gutes Design haben. Welche Daten benötigen Sie und wo? Welche Methoden sind Daten-zu-Daten-Methoden? Welche Abstraktionen benötigen Sie für E / A, Netzwerk, Ansicht usw.? unveränderliche Klassen, die keinen Zustand benötigen.

Ich denke, die meisten Probleme treten bei großen, fetten Datenobjekten auf, die sich selbst speichern, transformieren und zeichnen können. Dann möchten Sie sie beispielsweise an einen Renderer übergeben. Sie können also den Inhalt der Daten nicht kopieren, da dies für ein großes Datenobjekt zu teuer ist. Aber du willst auch nicht, dass es sich ändert, also brauchst du etwas, wie constdu denkst. Lassen Sie den Compiler Ihre Designfehler herausfinden. Wenn Sie diese Objekte jedoch nur in ein unveränderliches Datenobjekt aufteilen und die Methoden im verantwortlichen Modul implementieren, können Sie (nur) den Zeiger übergeben und die gewünschten Aktionen mit den Daten ausführen, während gleichzeitig die Unveränderlichkeit gewährleistet ist.

Ich weiß, dass es in C ++ möglich ist, einige Methoden constzu deklarieren und andere Methoden nicht zu deklarieren, weil sie Mutatoren sind. Und dann einige Zeit später brauchen Sie einen Cache und dann mutiert ein Getter ein Objekt (weil es faul lädt) und es ist nicht constmehr. Aber das war der springende Punkt dieser Abstraktion, oder? Sie wissen nicht, welcher Status bei jedem Aufruf geändert wird.

Verschwendung
quelle
1
"Also scheint const nur in Szenarien möglich zu sein, in denen die Klassen (oder die Typen, die Sie const deklarieren möchten) trivial sind ..." Persistente Datenstrukturen sind unveränderlich und nicht trivial. Sie werden sie in C ++ einfach nicht sehen, da so gut wie alle ihre internen Daten zwischen mehreren Instanzen gemeinsam nutzen, und C ++ keine Garbage Collection hat, damit dies funktioniert. "Ich denke, man kann leicht auf const verzichten, wenn man ein gutes Design hat." Unveränderliche Werte vereinfachen das Überlegen von Code erheblich.
Doval
+1 Danke. Könnten Sie bitte "const zu einer Klasse deklarieren" klären? Meinten Sie set class als const? Wenn ja, frage ich nicht danach - C ++ - Konstante funktioniert wie eine Ansicht, Sie können eine Konstante neben dem Objekt deklarieren, nicht eine Klasse (oder etwas, das in C ++ 11 geändert wurde). Außerdem gibt es ein Problem mit dem nächsten Absatz und dem Wort "leicht" - es gibt eine gute Frage zu C ++ - const in C #, und der damit verbundene Arbeitsaufwand ist von größter Bedeutung - für jeden Typ, den Sie const(im Sinne von C ++) eingeben möchten ) müssen Sie die Schnittstelle definieren, auch für alle Eigenschaften.
Greenoldman
1
Wenn Sie von einer "Klasse" sprechen, meinen Sie tatsächlich "Instanz einer Klasse", oder? Ich denke, das ist ein wichtiger Unterschied.
SVICK