Verwendung von 'const' für Funktionsparameter

397

Wie weit gehst du mit const? Machst du nur Funktionen, constwenn es nötig ist, oder gehst du das ganze Schwein und benutzt es überall? Stellen Sie sich zum Beispiel einen einfachen Mutator vor, der einen einzelnen booleschen Parameter akzeptiert:

void SetValue(const bool b) { my_val_ = b; }

Ist das consteigentlich sinnvoll? Persönlich entscheide ich mich dafür, es ausgiebig zu nutzen, einschließlich der Parameter, aber in diesem Fall frage ich mich, ob es sich lohnt?

Ich war auch überrascht zu erfahren, dass Sie constParameter in einer Funktionsdeklaration weglassen können, diese aber in die Funktionsdefinition aufnehmen können, z.

.h Datei

void func(int n, long l);

CPP-Datei

void func(const int n, const long l)

Gibt es einen Grund dafür? Es scheint mir ein wenig ungewöhnlich.

rauben
quelle
Ich stimme dir nicht zu. Die .h-Datei muss auch die const-Definitionen enthalten. Wenn nicht, generiert der Compiler einen Fehler, wenn const-Parameter an die Funktion übergeben werden, da der Prototyp in der .h-Datei nicht über die const-Definitionen verfügt.
Selwyn
10
Genau. :-) (Mit der Frage, nicht dem letzten Kommentar!) Wenn ein Wert im Hauptteil der Funktion nicht geändert werden sollte, kann dies dazu beitragen, dumme == oder = Fehler zu stoppen. Sie sollten niemals const in beide einfügen (wenn Es wird als Wert übergeben, Sie müssen es sonst tun.) Es ist jedoch nicht ernst genug, um darüber zu streiten!
Chris Huang-Leaver
19
@selwyn: Selbst wenn Sie ein const int an die Funktion übergeben, wird es kopiert (da es keine Referenz ist), und daher spielt die Konstanz keine Rolle.
Jalf
1
Dieselbe
Teilweise
5
Mir ist klar, dass dieser Beitrag ein paar Jahre alt ist, aber als neuer Programmierer habe ich mich genau diese Frage gestellt und bin auf dieses Gespräch gestoßen. Wenn eine Funktion einen Wert nicht ändern sollte, sei es eine Referenz oder eine Kopie des Wertes / Objekts, sollte sie meiner Meinung nach const sein. Es ist sicherer, selbstdokumentierend und debuggerfreundlicher. Selbst für die einfachste Funktion, die eine Anweisung enthält, verwende ich immer noch const.

Antworten:

187

Der Grund dafür ist, dass const für den Parameter nur lokal innerhalb der Funktion gilt, da er an einer Kopie der Daten arbeitet. Dies bedeutet, dass die Funktionssignatur sowieso wirklich dieselbe ist. Es ist wahrscheinlich ein schlechter Stil, dies viel zu tun.

Ich persönlich neige dazu, const nur für Referenz- und Zeigerparameter zu verwenden. Für kopierte Objekte spielt es keine Rolle, obwohl es sicherer sein kann, da es die Absicht innerhalb der Funktion signalisiert. Es ist wirklich ein Urteilsspruch. Ich neige jedoch dazu, const_iterator zu verwenden, wenn ich etwas schleife, und ich beabsichtige nicht, es zu ändern, also denke ich, dass jeder sein eigenes hat, solange die const-Korrektheit für Referenztypen streng eingehalten wird.

Greg Rogers
quelle
57
Ich kann dem Teil "schlechter Stil" nicht zustimmen. Das Löschen constvon Funktionsprototypen hat den Vorteil, dass Sie die Header-Datei nicht ändern müssen, wenn Sie constspäter aus dem Implementierungsteil löschen möchten.
Michał Górny
4
"Ich persönlich neige dazu, const nur für Referenz- und Zeigerparameter zu verwenden." Vielleicht sollten Sie das klarstellen: "Ich neige dazu, keine überflüssigen Qualifikationsmerkmale in Funktionsdeklarationen zu verwenden, sondern dort, constwo es einen nützlichen Unterschied macht."
Deduplikator
3
Ich bin mit dieser Antwort nicht einverstanden. Ich lehne mich in die andere Richtung und markiere Parameter, constwann immer dies möglich ist. es ist ausdrucksvoller. Wenn ich den Code eines anderen lese, benutze ich kleine Indikatoren wie diesen, um zu beurteilen, wie viel Sorgfalt er beim Schreiben seines Codes neben Dingen wie magischen Zahlen, Kommentaren und der richtigen Verwendung von Zeigern usw. verwendet.
Ultimater
4
int getDouble(int a){ ++a; return 2*a; }Versuche dies. Natürlich hat das ++adort nichts zu tun, aber es kann dort in einer langen Funktion gefunden werden, die von mehr als einem Programmierer über einen langen Zeitraum geschrieben wurde. Ich würde dringend empfehlen, zu schreiben, int getDouble( const int a ){ //... }dass beim Finden ein Kompilierungsfehler auftritt ++a;.
dom_beau
3
Es kommt darauf an, wer welche Informationen benötigt. Sie geben den Parameter nach Wert an, damit der Aufrufer nichts darüber wissen muss, was Sie (intern) damit tun. Schreiben Sie also class Foo { int multiply(int a, int b) const; }in Ihren Header. Bei Ihrer Implementierung ist es Ihnen wichtig, dass Sie versprechen, sich nicht zu ändern, aund bdies int Foo::multiply(const int a, const int b) const { }ist hier sinnvoll. (Nebenbemerkung: Sowohl der Aufrufer als auch die Implementierung kümmern sich darum, dass die Funktion ihr FooObjekt nicht ändert , daher die Konstante am Ende ihrer Deklaration.)
CharonX
415

"const ist sinnlos, wenn das Argument als Wert übergeben wird, da Sie das Objekt des Aufrufers nicht ändern."

Falsch.

Es geht darum, Ihren Code und Ihre Annahmen selbst zu dokumentieren.

Wenn an Ihrem Code viele Leute arbeiten und Ihre Funktionen nicht trivial sind, sollten Sie alles markieren, was Sie können. Wenn Sie Code mit industrieller Stärke schreiben, sollten Sie immer davon ausgehen, dass Ihre Mitarbeiter Psychopathen sind, die versuchen, Sie auf jede erdenkliche Weise zu erreichen (zumal es in Zukunft oft Sie selbst sind).

Außerdem, wie jemand bereits erwähnt, es könnte die Compiler optimiert die Dinge ein wenig helfen (obwohl es eine lange gedreht ist).

Rlerallut
quelle
41
Stimme voll und ganz zu. Es geht darum, mit Menschen zu kommunizieren und zu beschränken, was mit einer Variablen getan werden kann, auf das, was getan werden soll.
Len Holgate
19
Ich habe diesen abgelehnt. Ich denke, Sie verwässern das, was Sie anzeigen möchten, mit const, wenn Sie es auf einfache Argumente für die Übergabe von Werten anwenden.
Tonylo
26
Ich habe diesen abgestimmt. Durch Deklarieren des Parameters 'const' werden dem Parameter semantische Informationen hinzugefügt. Sie heben hervor, was der ursprüngliche Autor des Codes beabsichtigt hatte, und dies hilft bei der Wartung des Codes im Laufe der Zeit.
Richard Corden
13
@tonylo: du missverstehst. Hier geht es darum, eine lokale Variable innerhalb eines Codeblocks als const zu markieren (das ist zufällig eine Funktion). Ich würde das gleiche für jede lokale Variable tun. Es ist orthogonal zu einer API, die const-korrekt ist, was in der Tat auch wichtig ist.
Rlerallut
56
Und es kann Fehler in der Funktion auffangen. Wenn Sie wissen, dass ein Parameter nicht geändert werden sollte, bedeutet das Deklarieren von const, dass der Compiler Sie darüber informiert, wenn Sie ihn versehentlich ändern.
Adrian
156

Manchmal (zu oft!) Muss ich den C ++ - Code eines anderen entwirren. Und wir alle wissen, dass der C ++ - Code eines anderen fast per Definition ein komplettes Durcheinander ist :) Das erste, was ich mache, um den lokalen Datenfluss zu entschlüsseln, ist, const in jede Variablendefinition zu setzen, bis der Compiler anfängt zu bellen. Dies bedeutet auch const-qualifizierende Wertargumente, da es sich nur um ausgefallene lokale Variablen handelt, die vom Aufrufer initialisiert wurden.

Ah, ich wünschte, Variablen wären standardmäßig const und für Nicht-const-Variablen wäre veränderbar erforderlich :)

Constantin
quelle
4
"Ich wünschte, Variablen wären standardmäßig const" - ein Oxymoron? 8-) Im Ernst, wie "consting" alles hilft Ihnen, Code zu entwirren? Wenn der ursprüngliche Autor ein vermeintlich konstantes Argument geändert hat, woher wissen Sie dann, dass die Variable eine Konstante sein sollte? Darüber hinaus soll die überwiegende Mehrheit der (nicht argumentativen) Variablen ... Variablen sein. Der Compiler sollte also sehr bald nach dem Start des Prozesses unterbrochen werden, nicht wahr?
Ysap
8
@ysap, 1. Wenn ich const so oft wie möglich markiere, kann ich sehen, welche Teile sich bewegen und welche nicht. Nach meiner Erfahrung sind viele Einheimische de facto konstant, nicht umgekehrt. 2. "Konstante Variable" / "Unveränderliche Variable" mag als Oxymoron klingen, ist jedoch in funktionalen und einigen nicht funktionalen Sprachen Standard. siehe Rust zum Beispiel: doc.rust-lang.org/book/variable-bindings.html
Constantin
1
Unter bestimmten Umständen auch Standard in c ++; Zum Beispiel ist das Lambda [x](){return ++x;}ein Fehler. siehe hier
Anatolyg
10
Variablen sind " const" standardmäßig in Rust :)
Phoenix
Variablen müssen nicht unbedingt zuweisbar sein, damit sie variieren können. Der Wert, mit dem sie initialisiert werden, kann auch zur Laufzeit variieren.
Sphynx
80

Die folgenden zwei Zeilen sind funktional äquivalent:

int foo (int a);
int foo (const int a);

Natürlich können Sie aden Körper nicht ändern, foowenn er auf die zweite Weise definiert ist, aber es gibt keinen Unterschied von außen.

Was constwirklich nützlich ist, sind Referenz- oder Zeigerparameter:

int foo (const BigStruct &a);
int foo (const BigStruct *a);

Dies besagt, dass foo einen großen Parameter annehmen kann, möglicherweise eine Datenstruktur mit einer Größe von Gigabyte, ohne ihn zu kopieren. Außerdem sagt es dem Aufrufer: "Foo wird den Inhalt dieses Parameters nicht ändern." Durch das Übergeben einer const-Referenz kann der Compiler auch bestimmte Leistungsentscheidungen treffen.

*: Es sei denn, es wirft die Konstanz weg, aber das ist ein anderer Beitrag.

Ben Straub
quelle
3
Darum geht es in dieser Frage nicht; Natürlich ist es für Argumente, auf die verwiesen wird oder auf die verwiesen wird, eine gute Idee, const zu verwenden (wenn der Wert, auf den verwiesen wird oder auf den verwiesen wird, nicht geändert wird). Beachten Sie, dass in Ihrem Zeigerbeispiel nicht der Parameter const ist. es ist das, worauf der Parameter hinweist.
tml
> Durch Übergeben einer const-Referenz kann der Compiler auch bestimmte Leistungsentscheidungen treffen. klassischer Irrtum - der Compiler muss die Konstanz selbst bestimmen, das Schlüsselwort const hilft dabei nicht, dank Zeiger-Aliasing und const_cast
jheriko
73

Extra überflüssige Konstanten sind vom API-Standpunkt aus schlecht:

Wenn Sie in Ihren Code zusätzliche überflüssige Konstanten für vom Wert übergebene intrinsische Typparameter einfügen, wird Ihre API unübersichtlich, während dem Aufrufer oder API-Benutzer kein aussagekräftiges Versprechen gegeben wird (dies behindert nur die Implementierung).

Zu viele 'const' in einer API, wenn sie nicht benötigt werden, sind wie " weinender Wolf ". Irgendwann werden die Leute anfangen, 'const' zu ignorieren, weil es überall ist und die meiste Zeit nichts bedeutet.

Das Argument "reductio ad absurdum" für zusätzliche Konstanten in der API ist gut für diese ersten beiden Punkte. Wenn mehr Konstantenparameter gut sind, sollte jedes Argument, das eine Konstante enthalten kann, eine Konstante enthalten. Wenn es wirklich so gut wäre, möchten Sie, dass const die Standardeinstellung für Parameter ist und nur dann ein Schlüsselwort wie "veränderlich" hat, wenn Sie den Parameter ändern möchten.

Versuchen wir also, const einzufügen, wo immer wir können:

void mungerum(char * buffer, const char * mask, int count);

void mungerum(char * const buffer, const char * const mask, const int count);

Betrachten Sie die obige Codezeile. Die Deklaration ist nicht nur übersichtlicher und länger und schwerer zu lesen, sondern drei der vier 'const'-Schlüsselwörter können vom API-Benutzer sicher ignoriert werden. Die zusätzliche Verwendung von 'const' hat die zweite Zeile jedoch möglicherweise GEFÄHRLICH gemacht!

Warum?

Eine schnelle Fehlinterpretation des ersten Parameters char * const bufferkönnte den Eindruck erwecken, dass der übergebene Speicher im Datenpuffer nicht geändert wird. Dies ist jedoch nicht der Fall! Überflüssiges 'const' kann zu gefährlichen und falschen Annahmen über Ihre API führen, wenn es schnell gescannt oder falsch gelesen wird.


Überflüssige Konstanten sind auch vom Standpunkt der Code-Implementierung aus schlecht:

#if FLEXIBLE_IMPLEMENTATION
       #define SUPERFLUOUS_CONST
#else
       #define SUPERFLUOUS_CONST             const
#endif

void bytecopy(char * SUPERFLUOUS_CONST dest,
   const char *source, SUPERFLUOUS_CONST int count);

Wenn FLEXIBLE_IMPLEMENTATION nicht wahr ist, verspricht die API, die Funktion nicht wie folgt zu implementieren.

void bytecopy(char * SUPERFLUOUS_CONST dest,
   const char *source, SUPERFLUOUS_CONST int count)
{
       // Will break if !FLEXIBLE_IMPLEMENTATION
       while(count--)
       {
              *dest++=*source++;
       }
}

void bytecopy(char * SUPERFLUOUS_CONST dest,
   const char *source, SUPERFLUOUS_CONST int count)
{
       for(int i=0;i<count;i++)
       {
              dest[i]=source[i];
       }
}

Das ist ein sehr dummes Versprechen. Warum sollten Sie ein Versprechen abgeben, das Ihrem Anrufer überhaupt keinen Nutzen bringt und nur Ihre Implementierung einschränkt?

Beide sind absolut gültige Implementierungen derselben Funktion, sodass Sie nur unnötig eine Hand hinter den Rücken gebunden haben.

Darüber hinaus ist es ein sehr flaches Versprechen, das leicht (und rechtlich umgangen) werden kann.

inline void bytecopyWrapped(char * dest,
   const char *source, int count)
{
       while(count--)
       {
              *dest++=*source++;
       }
}
void bytecopy(char * SUPERFLUOUS_CONST dest,
   const char *source,SUPERFLUOUS_CONST int count)
{
    bytecopyWrapped(dest, source, count);
}

Schauen Sie, ich habe es trotzdem so implementiert, obwohl ich versprochen habe, es nicht zu tun - nur mit einer Wrapper-Funktion. Es ist wie wenn der Böse verspricht, jemanden in einem Film nicht zu töten und seinem Handlanger befiehlt, ihn stattdessen zu töten.

Diese überflüssigen Konstanten sind nicht mehr wert als ein Versprechen eines Filmbösewichts.


Aber die Fähigkeit zu lügen wird noch schlimmer:

Ich wurde aufgeklärt, dass Sie const in Header (Deklaration) und Code (Definition) nicht übereinstimmen können, indem Sie falsche const verwenden. Die const-happy-Befürworter behaupten, dies sei eine gute Sache, da Sie const nur in die Definition einfügen können.

// Example of const only in definition, not declaration
class foo { void test(int *pi); };
void foo::test(int * const pi) { }

Das Gegenteil ist jedoch der Fall ... Sie können eine falsche Konstante nur in die Deklaration einfügen und in der Definition ignorieren. Dies macht überflüssige Konstanten in einer API nur zu einer schrecklichen Sache und einer schrecklichen Lüge - siehe dieses Beispiel:

class foo
{
    void test(int * const pi);
};

void foo::test(int *pi) // Look, the const in the definition is so superfluous I can ignore it here
{
    pi++;  // I promised in my definition I wouldn't modify this
}

Alles, was die überflüssige Konstante tatsächlich tut, ist, den Code des Implementierers weniger lesbar zu machen, indem er gezwungen wird, eine andere lokale Kopie oder eine Wrapper-Funktion zu verwenden, wenn er die Variable ändern oder die Variable als Nicht-Konstanten-Referenz übergeben möchte.

Schauen Sie sich dieses Beispiel an. Welches ist besser lesbar? Ist es offensichtlich, dass der einzige Grund für die zusätzliche Variable in der zweiten Funktion darin besteht, dass ein API-Designer eine überflüssige Konstante eingegeben hat?

struct llist
{
    llist * next;
};

void walkllist(llist *plist)
{
    llist *pnext;
    while(plist)
    {
        pnext=plist->next;
        walk(plist);
        plist=pnext;    // This line wouldn't compile if plist was const
    }
}

void walkllist(llist * SUPERFLUOUS_CONST plist)
{
    llist * pnotconst=plist;
    llist *pnext;
    while(pnotconst)
    {
        pnext=pnotconst->next;
        walk(pnotconst);
        pnotconst=pnext;
    }
}

Hoffentlich haben wir hier etwas gelernt. Überflüssige Konstante ist ein API-überfüllter Schandfleck, ein nerviger Nörgler, ein flaches und bedeutungsloses Versprechen, ein unnötiges Hindernis und führt gelegentlich zu sehr gefährlichen Fehlern.

Adisak
quelle
9
Warum die Abstimmungen? Es ist viel hilfreicher, wenn Sie einen kurzen Kommentar zu einer Ablehnung abgeben.
Adisak
7
Der springende Punkt bei der Verwendung des Arguments const ist, dass die markierte Linie fehlschlägt (plist = pnext). Es ist eine angemessene Sicherheitsmaßnahme, das Funktionsargument unveränderlich zu halten. Ich stimme Ihrem Standpunkt zu, dass sie in Funktionsdeklarationen schlecht sind (da sie überflüssig sind), aber sie können ihre Zwecke im Implementierungsblock erfüllen.
Tuko
23
@Adisak Ich sehe an sich nichts Falsches an Ihrer Antwort, aber aus Ihren Kommentaren geht hervor, dass Ihnen ein wichtiger Punkt fehlt. Die Funktionsdefinition / Implementierung ist nicht Teil der API, die nur die Funktion Erklärung . Wie Sie bereits gesagt haben, ist das Deklarieren von Funktionen mit const-Parametern sinnlos und führt zu Unordnung. Benutzer der API müssen ihre Implementierung jedoch möglicherweise nie sehen. In der Zwischenzeit kann der Implementierer entscheiden, einige der Parameter in der Funktionsdefinition NUR aus Gründen der Klarheit zu qualifizieren, was vollkommen in Ordnung ist.
jw013
17
@ jw013 ist korrekt void foo(int)und hat void foo(const int)genau die gleiche Funktion, keine Überlastungen. ideone.com/npN4W4 ideone.com/tZav9R Die Konstante hier ist nur ein Implementierungsdetail des Funktionskörpers und hat keinen Einfluss auf die Überlastungsauflösung. Lassen Sie const für eine sicherere und übersichtlichere API aus der Deklaration heraus, fügen Sie jedoch const in die Definition ein , wenn Sie den kopierten Wert nicht ändern möchten .
Oktalist
3
@Adisak Ich weiß, dass dies alt ist, aber ich glaube, dass die korrekte Verwendung für eine öffentliche API umgekehrt wäre. Auf diese Weise machen Entwickler, die an den Interna arbeiten, keine Fehler, wie pi++wenn sie es nicht sollen.
Kaffee und Code
39

const sollte die Standardeinstellung in C ++ sein. So was :

int i = 5 ; // i is a constant

var int i = 5 ; // i is a real variable
QBziZ
quelle
8
Genau meine Gedanken.
Constantin
24
Die Kompatibilität mit C ist zu wichtig, zumindest für die Leute, die C ++ entwerfen, um dies überhaupt in Betracht zu ziehen.
4
Interessant, daran hatte ich noch nie gedacht.
Dan
6
Ebenso unsignedsollte die Standardeinstellung in C ++ gewesen sein. So: int i = 5; // i is unsignedund signed int i = 5; // i is signed.
hkBattousai
25

Als ich C ++ für meinen Lebensunterhalt codierte, habe ich alles festgelegt, was ich konnte. Die Verwendung von const ist eine großartige Möglichkeit, dem Compiler zu helfen, Ihnen zu helfen. Wenn Sie beispielsweise die Rückgabewerte Ihrer Methode festlegen, können Sie Tippfehler wie Folgendes vermeiden:

foo() = 42

als du meintest:

foo() == 42

Wenn foo () definiert ist, um eine nicht konstante Referenz zurückzugeben:

int& foo() { /* ... */ }

Der Compiler lässt Sie gerne dem vom Funktionsaufruf zurückgegebenen anonymen temporären Wert einen Wert zuweisen. Making it const:

const int& foo() { /* ... */ }

Beseitigt diese Möglichkeit.

Avdi
quelle
6
Mit welchem ​​Compiler hat das funktioniert? GCC gibt einen Fehler beim Versuch zu kompilieren foo() = 42: Fehler: lWert als linker Operand der Zuweisung erforderlich
Gavrie
Das ist einfach falsch. foo () = 42 ist dasselbe wie 2 = 3, dh ein Compilerfehler. Und die Rückgabe einer Konstante ist völlig sinnlos. Es macht nichts für einen eingebauten Typ.
Josh
2
Ich bin auf diese Verwendung von const gestoßen und kann Ihnen sagen, dass sie am Ende viel mehr Ärger als Vorteile verursacht. Hinweis: const int foo()ist von einem anderen Typ als int foo(), was Sie in große Schwierigkeiten bringt, wenn Sie Dinge wie Funktionszeiger, Signal- / Slot-Systeme oder boost :: bind verwenden.
Mephane
2
Ich habe den Code korrigiert, um den Referenzrückgabewert einzuschließen.
Avdi
Ist nicht const int& foo()effektiv dasselbe wie int foo()aufgrund der Rückgabewertoptimierung?
Zantier
15

Es gibt eine gute Diskussion zu diesem Thema in den alten "Guru der Woche" -Artikeln auf comp.lang.c ++., Die hier moderiert werden .

Der entsprechende GOTW Artikel ist auf Herb Sutter-Website verfügbar hier .

Leere
quelle
1
Herb Sutter ist ein wirklich kluger Kerl :-) Auf jeden Fall eine Lektüre wert und ich stimme ALLEN seinen Punkten zu.
Adisak
2
Nun, guter Artikel, aber ich bin nicht einverstanden mit ihm über die Argumente. Ich mache sie auch const, weil sie wie Variablen sind und ich möchte nie, dass jemand Änderungen an meinen Argumenten vornimmt.
QBziZ
9

Ich sage const Ihre Wertparameter.

Betrachten Sie diese Buggy-Funktion:

bool isZero(int number)
{
  if (number = 0)  // whoops, should be number == 0
    return true;
  else
    return false;
}

Wenn der Parameter number const wäre, würde der Compiler anhalten und uns vor dem Fehler warnen.


quelle
2
Ein anderer Weg ist die Verwendung von if (0 == number) ... else ...;
Johannes Schaub - Litb
5
@ ChrisHuang-Leaver Schrecklich ist es nicht, wenn Sie wie Yoda sprechen, tun Sie: stackoverflow.com/a/2430307/210916
MPelletier
GCC / Clang - Wall gibt Ihnen - Klammern, die verlangen, dass Sie es "if ((number = 0))" machen, wenn dies tatsächlich das ist, was Sie beabsichtigt haben. Was gut als Ersatz für Yoda funktioniert.
Jetski S-Typ
8

Ich verwende const für Funktionsparameter, die Referenzen (oder Zeiger) sind, die nur [in] Daten sind und von der Funktion nicht geändert werden. Das heißt, wenn der Zweck der Verwendung einer Referenz darin besteht, das Kopieren von Daten zu vermeiden und das Ändern des übergebenen Parameters nicht zuzulassen.

Wenn Sie in Ihrem Beispiel const auf den booleschen b-Parameter setzen, wird die Implementierung nur eingeschränkt und es wird kein Beitrag zur Schnittstelle der Klasse geleistet (obwohl normalerweise empfohlen wird, die Parameter nicht zu ändern).

Die Funktionssignatur für

void foo(int a);

und

void foo(const int a);

ist das gleiche, was Ihre .c und .h erklärt

Asaf

Asaf R.
quelle
6

Wenn Sie die Operatoren ->*oder verwenden .*, ist dies ein Muss.

Es hindert Sie daran, so etwas zu schreiben

void foo(Bar *p) { if (++p->*member > 0) { ... } }

was ich jetzt fast getan habe und was wahrscheinlich nicht das tut, was Sie beabsichtigen.

Was ich sagen wollte war

void foo(Bar *p) { if (++(p->*member) > 0) { ... } }

und wenn ich ein constdazwischen gesetzt hätte Bar *und p, hätte mir der Compiler das gesagt.

user541686
quelle
4
Ich würde sofort einen Verweis auf die Priorität von Operatoren überprüfen, wenn ich so viele Operatoren zusammenmischen möchte (wenn ich noch nicht 100% weiß), also ist IMO kein Problem.
mk12
5

Ah, eine schwierige Frage. Auf der einen Seite ist eine Deklaration ein Vertrag und es ist wirklich nicht sinnvoll, ein const-Argument als Wert zu übergeben. Wenn Sie sich andererseits die Funktionsimplementierung ansehen, geben Sie dem Compiler mehr Möglichkeiten zur Optimierung, wenn Sie eine Argumentkonstante deklarieren.

Nemanja Trifunovic
quelle
5

const ist sinnlos, wenn das Argument als Wert übergeben wird, da Sie das Objekt des Aufrufers nicht ändern.

const sollte beim Übergeben als Referenz bevorzugt werden, es sei denn, der Zweck der Funktion besteht darin, den übergebenen Wert zu ändern.

Schließlich kann und sollte eine Funktion, die das aktuelle Objekt (dies) nicht ändert, als const deklariert werden. Ein Beispiel ist unten:

int SomeClass::GetValue() const {return m_internalValue;}

Dies ist ein Versprechen, das Objekt, auf das dieser Aufruf angewendet wird, nicht zu ändern. Mit anderen Worten, Sie können anrufen:

const SomeClass* pSomeClass;
pSomeClass->GetValue();

Wenn die Funktion nicht const wäre, würde dies zu einer Compiler-Warnung führen.

Dan Hewett
quelle
5

Das Markieren von Wertparametern 'const' ist definitiv eine subjektive Sache.

Allerdings ziehe ich es tatsächlich vor, Werteparameter const zu markieren, genau wie in Ihrem Beispiel.

void func(const int n, const long l) { /* ... */ }

Der Wert zeigt für mich deutlich an, dass die Funktionsparameterwerte von der Funktion niemals geändert werden. Sie haben am Anfang den gleichen Wert wie am Ende. Für mich gehört es dazu, einen sehr funktionalen Programmierstil beizubehalten.

Für eine kurze Funktion ist es wohl eine Verschwendung von Zeit / Raum, die 'const' dort zu haben, da es normalerweise ziemlich offensichtlich ist, dass die Argumente nicht von der Funktion geändert werden.

Bei einer größeren Funktion handelt es sich jedoch um eine Form der Implementierungsdokumentation, die vom Compiler erzwungen wird.

Ich kann sicher sein, dass ich, wenn ich mit 'n' und 'l' rechne, diese Berechnung umgestalten / verschieben kann, ohne befürchten zu müssen, dass ein anderes Ergebnis erzielt wird, da ich einen Ort verpasst habe, an dem einer oder beide geändert wurden.

Da es sich um ein Implementierungsdetail handelt, müssen Sie die Werteparameter const im Header nicht deklarieren, genauso wie Sie die Funktionsparameter nicht mit denselben Namen deklarieren müssen, die die Implementierung verwendet.

Lloyd
quelle
4

Möglicherweise ist dies kein gültiges Argument. Wenn wir jedoch den Wert einer const-Variablen in einem Funktionscompiler erhöhen, erhalten wir einen Fehler: " Fehler: Inkrement des schreibgeschützten Parameters ". Das bedeutet, dass wir das Schlüsselwort const verwenden können, um zu verhindern, dass unsere Variablen innerhalb von Funktionen versehentlich geändert werden (was nicht / schreibgeschützt sein soll). Wenn wir es also versehentlich zur Kompilierungszeit gemacht haben, wird uns der Compiler dies mitteilen. Dies ist besonders wichtig, wenn Sie nicht der einzige sind, der an diesem Projekt arbeitet.

HarshaXsoad
quelle
3

Ich neige dazu, const zu verwenden, wo immer dies möglich ist. (Oder ein anderes geeignetes Schlüsselwort für die Zielsprache.) Ich mache dies nur, weil es dem Compiler ermöglicht, zusätzliche Optimierungen vorzunehmen, die er sonst nicht vornehmen könnte. Da ich keine Ahnung habe, wie diese Optimierungen aussehen könnten, mache ich es immer, auch wenn es albern erscheint.

Nach allem, was ich weiß, sieht der Compiler möglicherweise einen Parameter für den konstanten Wert und sagt: "Hey, diese Funktion ändert ihn sowieso nicht, sodass ich als Referenz übergeben und einige Taktzyklen speichern kann." Ich glaube nicht, dass es jemals so etwas tun würde, da es die Funktionssignatur ändert, aber es macht den Punkt. Vielleicht macht es eine andere Stack-Manipulation oder so ... Der Punkt ist, ich weiß es nicht, aber ich weiß, dass der Versuch, schlauer zu sein als der Compiler, nur dazu führt, dass ich beschämt werde.

C ++ hat etwas mehr Gepäck, mit der Idee der Konstantenkorrektheit, so dass es noch wichtiger wird.

jdmichal
quelle
Obwohl dies in einigen Fällen hilfreich sein könnte, vermute ich, dass die Möglichkeit, Optimierungen zu fördern, als Vorteil von dramatisch überbewertet wird const. Es geht vielmehr darum, die Absicht innerhalb der Implementierung anzugeben und später Thinkoes zu fangen (versehentlich die falsche lokale Variable zu erhöhen, weil dies nicht der Fall war const). Parallel dazu möchte ich hinzufügen, dass Compiler sehr willkommen sind, Funktionssignaturen in dem Sinne zu ändern, dass Funktionen inline eingefügt werden können und einmal inline die gesamte Funktionsweise geändert werden kann. Das Hinzufügen oder Entfernen von Referenzen, das Erstellen von 'Variablen'-Literalen usw. gehören
underscore_d
3

1. Beste Antwort basierend auf meiner Einschätzung:

Die Antwort von @Adisak ist nach meiner Einschätzung die beste Antwort hier. Beachten Sie, dass diese Antwort zum Teil die beste ist, da sie neben der Verwendung von Sound und durchdachter Logik auch die am besten abgesicherte mit echten Codebeispielen ist.

2. Meine eigenen Worte (in Übereinstimmung mit der besten Antwort):

  1. Für Pass-by-Value gibt es keinen Vorteil beim Hinzufügen const. Alles was es tut ist:
    1. Beschränken Sie den Implementierer darauf, jedes Mal eine Kopie erstellen zu müssen, wenn er einen Eingabeparameter im Quellcode ändern möchte (diese Änderung hätte ohnehin keine Nebenwirkungen, da das, was übergeben wird, bereits eine Kopie ist, da es als Wert übergeben wird). Und häufig wird das Ändern eines Eingabeparameters, der als Wert übergeben wird, zum Implementieren der Funktion verwendet, sodass das Hinzufügen constüberall dies behindern kann.
    2. und das constunnötige Überladen des Codes mit consts überall, wodurch die Aufmerksamkeit von den consts abgelenkt wird , die wirklich notwendig sind, um sicheren Code zu haben.
  2. Wenn es mit dem Umgang Zeigern oder Referenzen , jedoch constist von entscheidenden Bedeutung , wenn nötig, und muss verwendet werden, da sie unerwünschte Nebenwirkungen im Zusammenhang mit persistenten Änderungen verhindern außerhalb der Funktion, und daher jeder einzelner Zeiger oder eine Referenz muss verwenden , constwenn die Parm nur ein Eingang, keine Ausgabe. Die Verwendung const nur von Parametern, die als Referenz oder Zeiger übergeben werden, hat den zusätzlichen Vorteil, dass deutlich wird, welche Parameter Zeiger oder Referenzen sind. Es ist noch eine Sache, herauszustechen und zu sagen: "Achtung! Jeder Parameter constdaneben ist eine Referenz oder ein Zeiger!".
  3. Was ich oben beschrieben habe, war häufig der Konsens, der in professionellen Softwareorganisationen erzielt wurde, in denen ich gearbeitet habe, und wurde als Best Practice angesehen. Manchmal war sogar die Regel streng: "Verwenden Sie const niemals für Parameter, die als Wert übergeben werden, sondern immer für Parameter, die als Referenz oder Zeiger übergeben werden, wenn es sich nur um Eingaben handelt."

3. Googles Worte (stimme mir zu und die beste Antwort):

(Aus dem " Google C ++ Style Guide ")

Für einen Funktionswert, der als Wert übergeben wird, hat const keine Auswirkung auf den Aufrufer und wird daher in Funktionsdeklarationen nicht empfohlen. Siehe TotW # 109 .

Die Verwendung von const für lokale Variablen wird weder empfohlen noch empfohlen.

Quelle: Abschnitt "Verwendung von const" im Google C ++ - Style Guide: https://google.github.io/styleguide/cppguide.html#Use_of_const . Dies ist eigentlich ein wirklich wertvoller Abschnitt, lesen Sie also den gesamten Abschnitt.

Beachten Sie, dass "TotW # 109" für "Tipp der Woche # 109: Sinnvoll constin Funktionsdeklarationen" steht und auch eine nützliche Lektüre ist. Es ist informativer und weniger aussagekräftig, was zu tun ist, und basiert auf dem Kontext, der vor der constoben zitierten Google C ++ - Style Guide-Regel erstellt wurde. Aufgrund der Klarheit wurde die oben angegebene constRegel jedoch zu Google C ++ hinzugefügt Gestaltungsrichtlinie.

Beachten Sie auch, dass ich, obwohl ich hier zur Verteidigung meiner Position den Google C ++ - Style Guide zitiere, NICHT bedeutet, dass ich immer dem Guide folge oder immer empfehle, dem Guide zu folgen. Einige der Dinge, die sie empfehlen, sind einfach komisch, wie zum Beispiel ihre kDaysInAWeekNamenskonvention für "Konstante Namen" . Es ist jedoch immer noch nützlich und relevant, darauf hinzuweisen, wenn eines der erfolgreichsten und einflussreichsten technischen und Softwareunternehmen der Welt dieselbe Rechtfertigung verwendet wie ich und andere wie @Adisak, um unsere Standpunkte in dieser Angelegenheit zu untermauern.

4. Clangs Linter clang-tidyhat einige Optionen dafür:

A. Es ist auch erwähnenswert, dass Clang der Linter, clang-tidyhat eine Option, readability-avoid-const-params-in-decls, hier beschrieben , zu unterstützen , in einer Code - Basis Durchsetzung nicht mit constfür Pass-by-Value - Funktion Parameter :

Überprüft, ob eine Funktionsdeklaration Parameter enthält, die const der obersten Ebene sind.

const-Werte in Deklarationen wirken sich nicht auf die Signatur einer Funktion aus, daher sollten sie nicht dort abgelegt werden.

Beispiele:

void f(const string);   // Bad: const is top level.
void f(const string&);  // Good: const is not top level.

Und hier sind zwei weitere Beispiele, die ich der Vollständigkeit und Klarheit halber hinzufüge:

void f(char * const c_string);   // Bad: const is top level. [This makes the _pointer itself_, NOT what it points to, const]
void f(const char * c_string);   // Good: const is not top level. [This makes what is being _pointed to_ const]

B. Es gibt auch folgende Option: readability-const-return-type- https://clang.llvm.org/extra/clang-tidy/checks/readability-const-return-type.html

5. Mein pragmatischer Ansatz, wie ich einen Styleguide zu diesem Thema formulieren würde:

Ich würde dies einfach kopieren und in meinen Styleguide einfügen:

[KOPIEREN / EINFÜGEN STARTEN]

  1. Verwenden Sie diese const Funktion immer für Funktionsparameter, die als Referenz oder Zeiger übergeben werden, wenn deren Inhalt (auf den sie zeigen) NICHT geändert werden soll. Auf diese Weise wird deutlich, wann eine Variable, die als Referenz oder Zeiger übergeben wird, voraussichtlich geändert wird, da sie fehlt const. In diesem Anwendungsfall werden constversehentliche Nebenwirkungen außerhalb der Funktion verhindert.
  2. Es wird nicht empfohlen , constFunktionsparameter zu verwenden, die als Wert übergeben werden, da dies constkeine Auswirkungen auf den Aufrufer hat: Selbst wenn die Variable in der Funktion geändert wird, treten außerhalb der Funktion keine Nebenwirkungen auf. Weitere Begründungen und Einblicke finden Sie in den folgenden Ressourcen:
    1. "Google C ++ Style Guide" Abschnitt "Verwendung von const"
    2. "Tipp der Woche # 109: Sinnvoll constin Funktionserklärungen"
    3. Adisaks Stack Overflow-Antwort zu "Verwendung von 'const' für Funktionsparameter"
  3. " Verwenden Sie niemals Top-Level const[dh: constfür vom Wert übergebene Parameter] für Funktionsparameter in Deklarationen, die keine Definitionen sind (und achten Sie darauf, keine bedeutungslosen zu kopieren / einzufügen const). Es ist bedeutungslos und wird vom Compiler ignoriert, es ist visuelles Rauschen und es könnte die Leser irreführen "( https://abseil.io/tips/109 , Hervorhebung hinzugefügt).
    1. Die einzigen constQualifizierer, die sich auf die Kompilierung auswirken, sind diejenigen, die in der Funktionsdefinition platziert sind, NICHT diejenigen in einer Vorwärtsdeklaration der Funktion, wie z. B. in einer Funktions- (Methoden-) Deklaration in einer Header-Datei.
  4. Verwenden Sie niemals Top-Level const[dh: constvon Variablen als Wert übergeben ] auf Werte zurück durch eine Funktion.
  5. Die Verwendung constvon Zeigern oder Referenzen, die von einer Funktion zurückgegeben werden, liegt beim Implementierer , da dies manchmal nützlich ist.
  6. TODO: Erzwingen Sie einige der oben genannten clang-tidyOptionen mit den folgenden Optionen:
    1. https://clang.llvm.org/extra/clang-tidy/checks/readability-avoid-const-params-in-decls.html
    2. https://clang.llvm.org/extra/clang-tidy/checks/readability-const-return-type.html

Hier sind einige Codebeispiele, um die constoben beschriebenen Regeln zu demonstrieren :

constParameterbeispiele:
(einige sind hier ausgeliehen )

void f(const std::string);   // Bad: const is top level.
void f(const std::string&);  // Good: const is not top level.

void f(char * const c_string);   // Bad: const is top level. [This makes the _pointer itself_, NOT what it points to, const]
void f(const char * c_string);   // Good: const is not top level. [This makes what is being _pointed to_ const]

constBeispiele für Rückgabetypen:
(einige sind von hier ausgeliehen )

// BAD--do not do this:
const int foo();
const Clazz foo();
Clazz *const foo();

// OK--up to the implementer:
const int* foo();
const int& foo();
const Clazz* foo();

[COPY / PASTE END]

Gabriel Staples
quelle
2

In dem von Ihnen erwähnten Fall hat dies keine Auswirkungen auf Aufrufer Ihrer API, weshalb dies normalerweise nicht erfolgt (und im Header nicht erforderlich ist). Dies betrifft nur die Implementierung Ihrer Funktion.

Es ist keine besonders schlechte Sache, aber die Vorteile sind nicht so groß, da sie sich nicht auf Ihre API auswirken und die Eingabe hinzufügen, sodass dies normalerweise nicht der Fall ist.

Luke Halliwell
quelle
2

Ich verwende const nicht für wertübergebene Parameter. Dem Aufrufer ist es egal, ob Sie den Parameter ändern oder nicht, es handelt sich um ein Implementierungsdetail.

Was wirklich wichtig ist, ist, Methoden als const zu markieren, wenn sie ihre Instanz nicht ändern. Tun Sie dies, während Sie fortfahren, da Sie sonst entweder viel const_cast <> erhalten oder feststellen, dass zum Markieren einer Methode const viel Code geändert werden muss, da andere Methoden aufgerufen werden, die als const markiert werden sollten.

Ich neige auch dazu, lokale vars const zu markieren, wenn ich sie nicht ändern muss. Ich glaube, es erleichtert das Verständnis des Codes, indem es die "beweglichen Teile" leichter identifiziert.

Aurélien Gâteau
quelle
2

Ich benutze const wo ich kann. Konstante für Parameter bedeutet, dass sie ihren Wert nicht ändern sollten. Dies ist besonders wertvoll, wenn Sie als Referenz übergeben werden. const for function erklärt, dass die Funktion die Klassenmitglieder nicht ändern soll.

Dror Helfer
quelle
2

Zusammenfassen:

  • "Normalerweise ist const pass-by-value nicht sinnvoll und bestenfalls irreführend." Von GOTW006
  • Sie können sie jedoch wie bei Variablen in die CPP einfügen.
  • Beachten Sie, dass die Standardbibliothek keine const verwendet. ZB std::vector::at(size_type pos). Was für die Standardbibliothek gut genug ist, ist gut für mich.
YvesgereY
quelle
2
"Was für die Standardbibliothek gut genug ist, ist gut für mich" ist nicht immer wahr. Zum Beispiel verwendet die Standardbibliothek wie immer hässliche Variablennamen _Tmp- das möchten Sie nicht (eigentlich dürfen Sie sie nicht verwenden).
Anatolyg
1
@anatolyg dies ist ein Implementierungsdetail
Fernando Pelliccioni
2
OK, sowohl Variablennamen als auch const-qualifizierte Typen in Argumentlisten sind Implementierungsdetails. Was ich sagen möchte ist, dass die Implementierung der Standardbibliothek manchmal nicht gut ist. Manchmal können (und sollten) Sie es besser machen. Wann wurde der Code für die Standardbibliothek geschrieben - vor 10 Jahren? Vor 5 Jahren (einige neueste Teile davon)? Wir können heute besseren Code schreiben.
Anatolyg
1

Wenn der Parameter als Wert übergeben wird (und keine Referenz ist), gibt es normalerweise keinen großen Unterschied, ob der Parameter als const deklariert ist oder nicht (es sei denn, er enthält ein Referenzelement - kein Problem für integrierte Typen). Wenn der Parameter eine Referenz oder ein Zeiger ist, ist es normalerweise besser, den referenzierten / referenzierten Speicher zu schützen, nicht den Zeiger selbst (ich denke, Sie können die Referenz selbst nicht konstant machen, nicht, dass es wichtig ist, da Sie den Schiedsrichter nicht ändern können). . Es scheint eine gute Idee zu sein, alles zu schützen, was Sie als Konstante können. Sie können es weglassen, ohne befürchten zu müssen, einen Fehler zu machen, wenn es sich bei den Parametern nur um PODs (einschließlich integrierter Typen) handelt und keine Chance besteht, dass sie sich weiter entlang der Straße ändern (z. B. in Ihrem Beispiel der Parameter bool).

Ich wusste nichts über den Unterschied bei der Deklaration von .h / .cpp-Dateien, aber es macht Sinn. Auf der Ebene des Maschinencodes ist nichts "const". Wenn Sie also eine Funktion (in der .h) als nicht const deklarieren, ist der Code derselbe, als ob Sie ihn als const deklarieren (abgesehen von Optimierungen). Es hilft Ihnen jedoch, den Compiler zu registrieren, damit Sie den Wert der Variablen in der Implementierung der Funktion (.ccp) nicht ändern. Dies kann nützlich sein, wenn Sie von einer Schnittstelle erben, die Änderungen zulässt, Sie jedoch nicht zu Parametern wechseln müssen, um die erforderliche Funktionalität zu erreichen.

Attila
quelle
0

Ich würde solche Parameter nicht mit const versehen - jeder weiß bereits, dass ein Boolescher Wert (im Gegensatz zu einem Booleschen &) konstant ist. Wenn Sie ihn also hinzufügen, denken die Leute: "Warten Sie, was?" oder sogar, dass Sie den Parameter als Referenz übergeben.

Khoth
quelle
4
Manchmal möchten Sie ein Objekt als Referenz übergeben (aus Leistungsgründen), aber nicht ändern, sodass const dann obligatorisch ist. Es wäre dann eine gute Praxis, alle diese Parameter - auch bools - const beizubehalten, damit Ihr Code leichter zu lesen ist.
Gbjbaanb
0

Die Sache, an die man sich bei const erinnern sollte, ist, dass es viel einfacher ist, Dinge von Anfang an const zu machen, als zu versuchen, sie später einzufügen.

Verwenden Sie const, wenn etwas unverändert bleiben soll - ein zusätzlicher Hinweis, der beschreibt, was Ihre Funktion tut und was Sie erwartet. Ich habe viele C-APIs gesehen, die mit einigen von ihnen funktionieren könnten, insbesondere mit solchen, die C-Strings akzeptieren!

Ich würde eher das const-Schlüsselwort in der cpp-Datei weglassen als den Header, aber da ich dazu neige, sie auszuschneiden + einzufügen, würden sie an beiden Stellen aufbewahrt. Ich habe keine Ahnung, warum der Compiler das zulässt, ich denke, es ist eine Compilersache. Es wird auf jeden Fall empfohlen, Ihr const-Schlüsselwort in beide Dateien einzufügen.

gbjbaanb
quelle
Ich verstehe das überhaupt nicht. Warum neigen Sie dazu, es in der CPP-Datei wegzulassen (Funktionsdefinition)? Dort bedeutet es tatsächlich etwas und kann Fehler auffangen. Warum ist es Ihrer Meinung nach die beste Vorgehensweise, const an beiden Stellen zu platzieren? In der Header-Datei (Funktionsdeklaration) bedeutet dies nichts und überfrachtet die API. Vielleicht hat es einen kleinen Wert, dass Deklaration und Defn genau gleich aussehen, aber es scheint mir, dass dies ein wirklich kleiner Vorteil im Vergleich zum Problem der Unordnung in der API ist.
Don Hatch
@ DonHatch 8 Jahre später, wow. Wie auch immer, wie das OP sagte "Ich war auch überrascht zu erfahren, dass Sie const aus Parametern in einer Funktionsdeklaration weglassen, aber in die Funktionsdefinition aufnehmen können".
Gbjbaanb
0

Es gibt wirklich keinen Grund, einen Wertparameter "const" zu machen, da die Funktion sowieso nur eine Kopie der Variablen ändern kann.

Der Grund für die Verwendung von "const" liegt darin, dass Sie etwas Größeres (z. B. eine Struktur mit vielen Elementen) als Referenz übergeben. In diesem Fall wird sichergestellt, dass die Funktion es nicht ändern kann. oder besser gesagt, der Compiler wird sich beschweren, wenn Sie versuchen, es auf herkömmliche Weise zu ändern. Es verhindert, dass es versehentlich geändert wird.

MarkR
quelle
0

Der Parameter Const ist nur dann nützlich, wenn der Parameter als Referenz übergeben wird, dh entweder als Referenz oder als Zeiger. Wenn der Compiler einen const-Parameter sieht, stellt er sicher, dass die im Parameter verwendete Variable nicht im Hauptteil der Funktion geändert wird. Warum sollte jemand einen By-Value-Parameter als Konstante festlegen wollen? :-)


quelle
Aus vielen Gründen. Wenn Sie einen By-Value-Parameter festlegen, constheißt es eindeutig: „Ich muss dies nicht ändern, daher erkläre ich dies. Wenn ich später versuche, es zu ändern, geben Sie mir einen Fehler beim Kompilieren, damit ich meinen Fehler beheben oder die Markierung als deaktivieren kann const. ' Es geht also sowohl um Code-Hygiene als auch um Sicherheit. Bei allem, was zum Hinzufügen zu Implementierungsdateien erforderlich ist, sollte es sich um einen reinen Reflex handeln, IMO.
underscore_d
0

Da Parameter als Wert übergeben werden, spielt es keine Rolle, ob Sie const aus Sicht der aufrufenden Funktion angeben oder nicht. Grundsätzlich ist es nicht sinnvoll, Parameter für die Übergabe von Werten als const zu deklarieren.


quelle
0

Alle Konstanten in Ihren Beispielen haben keinen Zweck. C ++ ist standardmäßig eine Wertübergabe, daher erhält die Funktion Kopien dieser Ints und Booleschen Werte. Selbst wenn die Funktion sie ändert, ist die Kopie des Anrufers nicht betroffen.

Also würde ich zusätzliche Konstanten vermeiden, weil

  • Sie sind überflüssig
  • Sie überladen den Text
  • Sie hindern mich daran, den übergebenen Wert in Fällen zu ändern, in denen er nützlich oder effizient sein könnte.
AShelly
quelle
-1

Ich weiß, dass die Frage "ein bisschen" veraltet ist, aber als ich darauf stieß, könnte es auch jemand anderes in Zukunft tun ... ... ich bezweifle immer noch, dass der arme Kerl hier unten auflisten wird, um meinen Kommentar zu lesen :)

Es scheint mir, dass wir uns immer noch zu sehr auf die Denkweise im C-Stil beschränken. Im OOP-Paradigma spielen wir mit Objekten herum, nicht mit Typen. Das Const-Objekt kann sich konzeptionell von einem Nicht-Const-Objekt unterscheiden, insbesondere im Sinne von Logical-Const (im Gegensatz zu Bitwise-Const). Selbst wenn die Konstantenkorrektheit von Funktionsparametern (möglicherweise) bei PODs eine Übervorsicht ist, ist dies bei Objekten nicht der Fall. Wenn eine Funktion mit einem const-Objekt arbeitet, sollte sie dies sagen. Betrachten Sie das folgende Code-Snippet

#include <iostream>

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class SharedBuffer {
private:

  int fakeData;

  int const & Get_(int i) const
  {

    std::cout << "Accessing buffer element" << std::endl;
    return fakeData;

  }

public:

  int & operator[](int i)
  {

    Unique();
    return const_cast<int &>(Get_(i));

  }

  int const & operator[](int i) const
  {

    return Get_(i);

  }

  void Unique()
  {

    std::cout << "Making buffer unique (expensive operation)" << std::endl;

  }

};

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void NonConstF(SharedBuffer x)
{

  x[0] = 1;

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ConstF(const SharedBuffer x)
{

  int q = x[0];

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int main()
{

  SharedBuffer x;

  NonConstF(x);

  std::cout << std::endl;

  ConstF(x);

  return 0;

}

ps.: Sie können argumentieren, dass (const) Referenz hier angemessener wäre und Ihnen das gleiche Verhalten gibt. Nun, richtig. Ich gebe nur ein anderes Bild als das, was ich anderswo sehen konnte ...

PavDub
quelle