Const vor oder const nach?

145

Zu Beginn wissen Sie wahrscheinlich, dass constdamit entweder die Daten eines Objekts oder ein Zeiger nicht geändert werden können oder beides.

const Object* obj; // can't change data
Object* const obj; // can't change pointer
const Object* const obj; // can't change data or pointer

Sie können jedoch auch die folgende Syntax verwenden:

Object const *obj; // same as const Object* obj;

Das einzige, was wichtig zu sein scheint, ist, auf welcher Seite des Sterns Sie das constSchlüsselwort setzen. Persönlich ziehe ich es vor, constlinks vom Typ zu setzen, um anzugeben, dass die Daten nicht geändert werden können, da ich finde, dass sie in meiner Denkweise von links nach rechts besser lesbar sind, aber welche Syntax kam zuerst?

Noch wichtiger ist, warum gibt es zwei korrekte Möglichkeiten zur Angabe von constDaten und in welcher Situation würden Sie eine über die andere bevorzugen oder benötigen, wenn überhaupt?

Bearbeiten:

Es klingt also so, als wäre dies eine willkürliche Entscheidung gewesen, als der Standard für die Interpretation von Dingen durch Compiler lange vor meiner Geburt ausgearbeitet wurde. Da constes auf das angewendet wird, was sich links vom Schlüsselwort befindet (standardmäßig?), Vermutlich hat es keinen Schaden angerichtet, "Verknüpfungen" hinzuzufügen , um Schlüsselwörter und Typqualifizierer auf andere Weise anzuwenden, zumindest bis sich die Deklaration um ändert Analyse eines * oder & ...

Dies war auch in C der Fall, nehme ich an?

AJG85
quelle
9
Fügen Sie in Makros immer const nach dem Typ hinzu, z. B. #define MAKE_CONST(T) T conststatt #define MAKE_CONST(T) const T, MAKE_CONST(int *)damit korrekt auf int * conststatt erweitert wird const int *.
Jotik
6
Ich habe diese beiden Stile gesehen, die als "East Const" und "West Const" bezeichnet werden.
Tom Anderson
13
@ TomAnderson, aber eigentlich sollte es "East Const" und "Const West" sein.
YSC

Antworten:

96

Warum gibt es zwei korrekte Möglichkeiten, constDaten anzugeben , und in welcher Situation würden Sie eine über die andere bevorzugen oder benötigen, wenn überhaupt?

Der Grund dafür, dass die Position constinnerhalb von Spezifizierern vor einem Sternchen keine Rolle spielt, ist im Wesentlichen, dass die C-Grammatik von Kernighan und Ritchie auf diese Weise definiert wurde.

Der Grund, warum sie die Grammatik auf diese Weise definiert haben, war wahrscheinlich, dass ihr C-Compiler die Eingabe von links nach rechts analysierte und die Verarbeitung jedes Tokens beendete, während er dies verbrauchte. Durch das Konsumieren des *Tokens wird der Status der aktuellen Deklaration in einen Zeigertyp geändert. Begegnung constnach *bedeutet, dass das constQualifikationsmerkmal auf eine Zeigerdeklaration angewendet wird. Wenn Sie darauf stoßen, bevor *das Qualifikationsmerkmal auf die Daten angewendet wird, auf die verwiesen wird.

Da sich die semantische Bedeutung nicht ändert, wenn das constQualifikationsmerkmal vor oder nach den Typspezifizierern angezeigt wird, wird es in beiden Fällen akzeptiert.

Ein ähnlicher Fall tritt auf, wenn Funktionszeiger deklariert werden, wobei:

  • void * function1(void)deklariert eine Funktion, die zurückgibt void *,

  • void (* function2)(void)deklariert einen Funktionszeiger auf eine Funktion, die zurückgibt void.

Wiederum ist zu beachten, dass die Sprachsyntax einen Parser von links nach rechts unterstützt.

Heath Hunnicutt
quelle
7
Kernighan war Co-Autor des Buches, war aber nicht am Design von C beteiligt, sondern nur an Ritchie.
Tom Zych
8
Ich konnte mich nie erinnern, welches welches ist. Dank Ihrer Erklärung habe ich endlich Mnemotechnik, um mich daran zu erinnern. Vielen Dank! Bevor der *Compiler-Parser nicht weiß, dass es sich um einen Zeiger handelt, handelt es sich um eine Konstante für den Datenwert. Nach * bezieht es sich auf den konstanten Zeiger. Brillant. Und schließlich erklärt es, warum ich const charso gut kann char const.
Vit Bernatik
2
Die Vermutung, warum es so gemacht wurde, scheint mir eher schwach / widersprüchlich. Das heißt, wenn ich eine Sprache definieren und einen Compiler schreiben würde, und ich wollte es einfach halten und "Eingaben von links nach rechts analysieren und die Verarbeitung jedes Tokens beenden, während es verbraucht wird", wie Sie sagen, scheint es mir Ich würde verlangen, dass das constimmer nach dem kommt, was es qualifiziert ... genau so, dass ich die Verarbeitung der Konstante immer sofort beenden kann, nachdem ich sie verbraucht habe. Dies scheint also ein Argument dafür zu sein , West-Const zu verbieten , anstatt es zuzulassen.
Don Hatch
3
"Da sich die semantische Bedeutung nicht ändert, wenn das const-Qualifikationsmerkmal vor oder nach den Typspezifizierern angezeigt wird, wird es in beiden Fällen akzeptiert." Ist das nicht Zirkelschluss? Die Frage ist, warum die semantische Bedeutung so definiert ist, also glaube ich nicht, dass dieser Satz etwas beiträgt.
Don Hatch
1
@donhatch Sie müssen sich daran erinnern, dass Sprachen im Vergleich zu heute und den Annahmen, die wir aufgrund unserer Vertrautheit mit gutem Programmiersprachen-Design treffen, damals ziemlich neue Dinge waren. Auch ob man eine freizügige oder eingeschränkte Sprache hat, ist ein Werturteil. Sollte Python beispielsweise einen ++Operator haben? "Der Satz", imho, half mir zu erkennen, dass es keinen bestimmten Grund gab, außer weil sie es konnten. Vielleicht würden sie heute eine andere Wahl treffen / vielleicht auch nicht.
Ragerdl
75

Die Regel lautet:

const gilt für das übrig gebliebene. Wenn links nichts ist, dann gilt das für das Ding rechts davon.

Ich bevorzuge die Verwendung von const rechts von der Sache, um const zu sein, nur weil es die "ursprüngliche" Art ist, wie const definiert wird.

Aber ich denke, das ist eine sehr subjektive Sichtweise.

horstforst
quelle
18
Ich ziehe es vor, es links zu platzieren, aber ich denke, es ist sinnvoller, es rechts zu platzieren. Im Allgemeinen lesen Sie Typen in C ++ von rechts nach links, z. B. Object const *als Zeiger auf ein const-Objekt. Wenn Sie constdas links platzieren, wird es als Zeiger auf ein Objekt gelesen, das const ist und nicht wirklich gut fließt.
Collin Dauphinee
1
Ich habe den Eindruck, dass auf der linken Seite die Übereinstimmung mit anderen Arten von C-Deklarationen im menschlichen Stil steht (computertechnisch ist dies nicht korrekt, da constes sich nicht um eine Speicherklasse handelt, aber die Leute sind keine Parser).
Geekosaurier
1
@Heath Ich glaube, das ist eher eine Richtlinie als eine Regel und ich habe es oft gehört, um mich daran zu erinnern, wie der Compiler es interpretieren wird ... Ich verstehe, wie es funktioniert, also war ich nur neugierig auf den Denkprozess hinter dem Entscheidung, es in beide Richtungen zu unterstützen.
AJG85
3
@HeathHunnicutt die Regel existiert, aber es ist nur ein wenig komplizierter: c-faq.com/decl/spiral.anderson.html
imallett
2
@HeathHunnicutt: Die Spiralregel ist die erweiterte Version des Kommentars des ersten Kommentators "Sie lesen im Allgemeinen Typen in [C /] C ++ von rechts nach links". Ich nahm an, Sie widersprachen dem. Ich denke jedoch, dass Sie sich stattdessen auf die Antwort selbst bezogen haben.
Imallett
56

Ich bevorzuge die zweite Syntax. Es hilft mir, den Überblick darüber zu behalten, was konstant ist, indem ich die Typdeklaration von rechts nach links lese:

Object * const obj;        // read right-to-left:  const pointer to Object
Object const * obj;        // read right-to-left:  pointer to const Object
Object const * const obj;  // read right-to-left:  const pointer to const Object
Matt Davis
quelle
3
Genau. Ein "konstanter Zeiger auf ein konstantes Objekt" ist Object const* constnicht const const Object*. "const" kann nicht links stehen, außer in dem speziellen Fall, in dem so viele Leute es absolut lieben. (Siehe Heide oben.)
cdunn2001
38

Die Reihenfolge der Schlüsselwörter in einer Deklaration ist nicht allzu fest. Es gibt viele Alternativen zu "der einen wahren Ordnung". So was

int long const long unsigned volatile i = 0;

oder sollte es sein

volatile unsigned long long int const i = 0;

??

Bo Persson
quelle
25
+1 für eine völlig verwirrende Definition einer einfachen Variablen. :)
Xeo
@rubenvb - Ja, leider sind sie. Die Grammatik sagt nur, dass a decl-specifier-seqeine Folge von decl-specifiers ist. Die Grammatik gibt keine Reihenfolge vor, und die Anzahl der Vorkommen für jedes Schlüsselwort ist nur durch einige semantische Regeln begrenzt (Sie können eine, constaber zwei habenlong :-)
Bo Persson
3
@rubenvb - Ja, unsignedist ein Typ, der mit unsigned intund identisch ist int unsigned. unsigned longist ein anderer Typ, der gleiche wie unsigned long intund int long unsigned. Sehen Sie das Muster?
Bo Persson
2
@Bo: Ich sehe das Durcheinander, muss drei haben, um ein Muster zu sehen ;). OK, danke
rubenvb
1
Früher konnten Sie staticdas Durcheinander von Wörtern erweitern, aber erst kürzlich haben sich Compiler beschwert, dass dies statican erster Stelle stehen muss.
Mark Lakata
8

Die erste Regel besteht darin, das Format zu verwenden, das Ihre lokalen Codierungsstandards erfordern. Danach: constWenn Sie die Front vorlegen, führt dies zu Verwirrung, wenn es sich um Typedefs handelt, z.

typedef int* IntPtr;
const IntPtr p1;   // same as int* const p1;

Wenn Ihr Codierungsstandard Typedefs von Zeigern zulässt, sollte er wirklich darauf bestehen, die Konstante nach dem Typ zu setzen. In jedem Fall, aber wenn es auf den Typ angewendet wird, muss const dem folgen, wofür es gilt, daher spricht Kohärenz auch für die Konstante danach. Lokale Kodierungsrichtlinien übertreffen jedoch alle diese; Der Unterschied ist normalerweise nicht wichtig genug, um den gesamten vorhandenen Code zu ändern.

James Kanze
quelle
Ich denke, das könnte den Grund hervorheben, warum wir in unseren eher lose definierten Standards in diesem Shop keine Typedefs von Zeigern haben.
AJG85
1
Meine Politik (wenn ich mich alleine entscheiden kann) ist es, die Konstante nach (aus Gründen der Kohärenz) zu setzen und keine typedefs für Zeiger zu verwenden (oder typedefs im Allgemeinen) :-). Übrigens sollte string :: iterator vs. string :: const_iterator wahrscheinlich auch bei Ihrer Entscheidung berücksichtigt werden. (Nur um die Dinge zu verwirren :-). Es gibt keine richtige Antwort.)
James Kanze
Ah ja, ich hätte das Verhalten const std::string::const_iteratorauch für ein gutes Maß
einbeziehen können
2
@JamesKanze - Moment mal, hilf mir hier raus ... Ich sehe die Verwirrung im geposteten Beispiel nicht. Was könnte const IntPtr p1möglicherweise anderes bedeuten als "konstanter ganzzahliger Zeiger" (dh "konstanter Zeiger auf ganze Zahl")? Niemand, der bei klarem Verstand ist, selbst wenn er nicht weiß, wie er IntPtrdefiniert ist, würde das p1für veränderlich halten. Und warum sollte jemand fälschlicherweise annehmen, dass dies *p1unveränderlich ist? Wenn Sie die Konstante an einer anderen Stelle platzieren (z. B. IntPtr const p1), ändert sich die Semantik überhaupt nicht.
Todd Lehman
3
@ToddLehman Sie werden die Verwirrung vielleicht nicht sehen, aber die meisten C ++ - Programmierer tun dies und verstehen es systematisch falsch (ohne Zweifel helfen Dinge wie std::vector<T>::const_iterator, wo es nicht der Iterator ist, der const ist, sondern worauf er zeigt).
James Kanze
7

Es gibt historische Gründe, warum entweder links oder rechts akzeptabel ist. Stroustrup hatte C ++ bis 1983 const hinzugefügt , aber es schaffte es nicht bis C89 / C90 nach C.

In C ++ gibt es einen guten Grund, const immer rechts zu verwenden. Sie werden im Einklang überall da konstanten Elementfunktionen müssen auf diese Weise erklärt werden:

int getInt() const;
Nick Westgate
quelle
1
... nun, der "gute Grund" ist nicht so überzeugend, weil andere mögliche Orte für die "const" nicht dasselbe bedeuten würden. const int& getInt(); int& const getInt();
Maestro
1
@ Maestro: Ich schlage vor, es int const& getInt();ist besser als das Äquivalent, const int& getInt();während das int& const getInt();, mit dem Sie es vergleichen, überflüssig ist (Referenzen sind bereits const), obwohl es legal ist und normalerweise eine Warnung gibt. Wie auch immer, const on a member function ändert den thisZeiger in der Funktion von Foo* constto Foo const* const.
Nick Westgate
constauf einer Mitgliedsfunktion bedeutet überhaupt nicht dasselbe - oder ist es void set(int)&;eine Art Verweis auf eine Funktion?
Davis Herring