Const prima o const dopo?


146

Per iniziare, probabilmente sai che constpuò essere utilizzato per rendere i dati di un oggetto o un puntatore non modificabili o entrambi.

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

Tuttavia puoi anche usare la sintassi:

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

L'unica cosa che sembra avere importanza è da che parte dell'asterisco inserisci la constparola chiave. Personalmente preferisco mettere consta sinistra del tipo per specificare che i suoi dati non sono modificabili in quanto trovo che leggano meglio nella mia mentalità da sinistra a destra ma quale sintassi è arrivata per prima?

Ancora più importante perché ci sono due modi corretti per specificare i constdati e in quale situazione preferiresti o avresti bisogno l'uno rispetto all'altro?

Modificare:

Quindi sembra che questa sia stata una decisione arbitraria quando lo standard su come i compilatori dovrebbero interpretare le cose è stato redatto molto prima che io nascessi. Poiché constviene applicato a ciò che si trova a sinistra della parola chiave (per impostazione predefinita?) Immagino che abbiano pensato che non ci fosse nulla di male nell'aggiungere "scorciatoie" per applicare parole chiave e digitare qualificatori in altri modi almeno fino a quando la dichiarazione cambia da analizzando un * o & ...

Questo è stato il caso anche in C, quindi presumo?


9
Nelle macro aggiungere sempre const dopo il tipo, ad es. #define MAKE_CONST(T) T constInvece di in #define MAKE_CONST(T) const Tmodo che MAKE_CONST(int *)si espanda correttamente int * constinvece di const int *.
jotik,

7
Ho visto questi due stili indicati come "const orientale" e "const occidentale".
Tom Anderson,

15
@ TomAnderson, ma in realtà dovrebbe essere "const const" e "const west".
YSC,

Risposte:


96

perché esistono due modi corretti per specificare i constdati e in quale situazione preferiresti o ne avresti bisogno?

In sostanza, il motivo per cui la posizione constall'interno degli specificatori prima di un asterisco non è importante è che la grammatica C è stata definita in questo modo da Kernighan e Ritchie.

Il motivo per cui hanno definito la grammatica in questo modo era probabile che il loro compilatore C analizzasse l'input da sinistra a destra e finisse di elaborare ciascun token man mano che lo consumava. Il consumo del *token modifica lo stato della dichiarazione corrente in un tipo di puntatore. L'incontro constdopo *significa che il constqualificatore viene applicato a una dichiarazione puntatore; incontrarlo prima del *modo in cui il qualificatore viene applicato ai dati indicati.

Poiché il significato semantico non cambia se il constqualificatore appare prima o dopo gli identificatori del tipo, viene accettato in entrambi i modi.

Un simile caso si presenta quando si dichiarano i puntatori a funzioni, dove:

  • void * function1(void)dichiara una funzione che ritorna void *,

  • void (* function2)(void)dichiara un puntatore a una funzione che ritorna void.

Ancora una volta la cosa da notare è che la sintassi del linguaggio supporta un parser da sinistra a destra.


7
Kernighan è stato coautore del libro ma non è stato coinvolto nella progettazione di C, solo Ritchie.
Tom Zych,

8
Non potrei mai ricordare quale sia quale. Grazie alla tua spiegazione, finalmente devo ricordare a me stesso. Grazie! Prima *che il parser del compilatore non sappia che è puntatore, quindi è const per il valore dei dati. Dopo * è correlato al puntatore costante. Brillante. E infine spiega perché posso fare const charaltrettanto char const.
Vit Bernatik,

2
L'ipotesi sul perché sia ​​stato fatto in questo modo mi sembra piuttosto debole / auto-contraddittoria. Cioè, se stavo definendo una lingua e scrivendo un compilatore, e volessi mantenerlo semplice e "analizzare l'input da sinistra a destra e terminare l'elaborazione di ogni token mentre lo consuma", come dici tu, mi sembra Avrei bisogno constdi venire sempre dopo che la cosa è qualificante ... esattamente così potrei sempre finire di elaborare la const immediatamente dopo averla consumata. Quindi questo sembra essere un argomento per proibire la const-ovest, piuttosto che permetterlo.
Don Hatch,

3
"Poiché il significato semantico non cambia se il qualificatore const appare prima o dopo gli identificatori del tipo, è accettato in entrambi i modi." Non è questo ragionamento circolare? La domanda è: perché il significato semantico è definito in questo modo, quindi non credo che questa frase offra qualcosa.
Don Hatch,

1
@donhatch Devi ricordare che, rispetto a oggi e alle ipotesi che facciamo sulla base della nostra familiarità con una buona progettazione del linguaggio di programmazione, allora i linguaggi erano cose piuttosto nuove. Inoltre, se uno ha un linguaggio permissivo o limitato è un giudizio di valore. Ad esempio, Python dovrebbe avere un ++operatore? "La frase", imho, mi ha aiutato a rendermi conto che non c'erano motivi particolari se non quello che potevano. Forse avrebbero fatto una scelta diversa oggi / forse no.
ragerdl,

75

La regola è:

const si applica alla cosa rimasta. Se non c'è nulla a sinistra, allora si applica alla cosa giusta.

Preferisco usare const a destra della cosa per essere const solo perché è il modo "originale" di definire const.

Ma penso che questo sia un punto di vista molto soggettivo.


18
Preferisco metterlo a sinistra, ma penso che metterlo a destra abbia più senso. In genere leggi i tipi in C ++ da destra a sinistra, ad esempio Object const *è un puntatore a un oggetto const. Se lo metti consta sinistra, leggerebbe come un puntatore a un oggetto che è const, che in realtà non scorre molto bene.
Collin Dauphinee,

1
Ho l'impressione che a sinistra sia per coerenza in stile umano con altri tipi di dichiarazioni C (dal punto di vista del computer non è corretto in quanto constnon è una classe di archiviazione, ma le persone non sono parser).
Geekosaur,

1
@Heath Credo che sia più una linea guida che una regola e l'ho sentito spesso come un modo per ricordare come il compilatore lo interpreterà ... Capisco come funziona, quindi ero solo curioso del processo di pensiero dietro decisione di sostenerlo in entrambi i modi.
AJG85

3
@HeathHunnicutt la regola esiste, ma è solo un po 'più complicata: c-faq.com/decl/spiral.anderson.html
imallett

2
@HeathHunnicutt: la regola della spirale è la versione espansa del commento del primo commentatore "In genere leggi i tipi in [C /] C ++ da destra a sinistra". Presumo che tu stia contraddicendo questo. Tuttavia, penso che potresti invece aver fatto riferimento alla risposta stessa.
imallett,

57

Preferisco la seconda sintassi. Mi aiuta a tenere traccia di "cosa" è costante leggendo la dichiarazione del tipo da destra a sinistra:

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

3
Esattamente. Un "puntatore costante a un oggetto costante" Object const* constnon lo è const const Object*. "const" non può essere sulla sinistra, tranne nel caso speciale in cui così tante persone lo adorano assolutamente. (Vedi Heath sopra.)
cdunn2001,

38

L'ordine delle parole chiave in una dichiarazione non è poi tutto risolto. Esistono molte alternative a "l'unico vero ordine". Come questo

int long const long unsigned volatile i = 0;

o dovrebbe essere

volatile unsigned long long int const i = 0;

??


25
+1 per una definizione totalmente confusa di una semplice variabile. :)
Xeo

@rubenvb - Sì, sfortunatamente lo sono. La grammatica dice solo che a decl-specifier-seqè una sequenza di decl-specifiers. Non c'è un ordine dato dalla grammatica e il numero di occorrenze per ogni parola chiave è limitato solo da alcune regole semantiche (puoi avere una constma due long:-)
Bo Persson,

3
@rubenvb - Sì, unsignedè un tipo, lo stesso di unsigned inte int unsigned. unsigned longè un altro tipo, lo stesso di unsigned long inte int long unsigned. Vedi lo schema?
Bo Persson,

2
@Bo: vedo il casino, devo averne tre per vedere uno schema ;). OK, grazie
rubenvb,

1
Un tempo eri in grado di aggiungere statical miscuglio di parole, ma solo di recente si sono lamentati dei compilatori che staticdevono venire prima.
Mark Lakata,

8

La prima regola è quella di utilizzare qualsiasi formato richiesto dagli standard di codifica locali. Dopodiché: mettere constin primo piano non c'è fine alla confusione quando sono coinvolti i typedef, ad esempio:

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

Se il tuo standard di codifica consente a typedef di puntatori, allora dovrebbe davvero insistere nel mettere la const dopo il tipo. In ogni caso, ma quando applicato al tipo, const deve seguire ciò a cui si applica, quindi la coerenza sostiene anche a favore di const dopo. Ma le linee guida sulla codifica locale vincono tutte queste cose; la differenza non è normalmente abbastanza importante per tornare indietro e cambiare tutto il codice esistente.


Penso che ciò possa evidenziare il motivo per cui in questo negozio non abbiamo typedef di puntatori nei nostri standard piuttosto vagamente definiti.
AJG85

1
La mia politica (quando decido da solo) è di mettere la const dopo (per motivi di coerenza) e di non usare typedefs per i puntatori (o tipicamente molto in generale) :-). E a proposito, string :: iterator vs. string :: const_iterator dovrebbe probabilmente essere preso in considerazione anche nella tua decisione. (Solo per confondere le cose :-). Non c'è una risposta giusta.)
James Kanze

Ah, sì, avrei potuto includere anche il comportamento di const std::string::const_iteratorper buona misura;)
AJG85

2
@JamesKanze - Aspetta un attimo, aiutami qui ... Non vedo la confusione nell'esempio pubblicato. Cos'altro potrebbe const IntPtr p1significare altro che "puntatore a numero intero costante" (ovvero "puntatore a numero intero costante")? Nessuno nella loro mente giusta, anche senza sapere come IntPtrè definito, potrebbe pensare che p1sia mutevole. E del resto, perché qualcuno dovrebbe erroneamente presumere che *p1sia immutabile? Inoltre, posizionare la const in qualsiasi altro luogo (ad esempio, IntPtr const p1), non cambia affatto la semantica.
Todd Lehman,

3
@ToddLehman Potresti non vedere la confusione, ma la maggior parte dei programmatori C ++ lo fa e sistematicamente sbagliare (senza dubbio aiutato da cose come std::vector<T>::const_iterator, dove non è l'iteratore che è const, ma a cosa punta).
James Kanze,

7

Ci sono ragioni storiche che sono accettabili sia a destra che a sinistra. Stroustrup aveva aggiunto const a C ++ entro il 1983 , ma non arrivò a C fino a C89 / C90.

In C ++ c'è una buona ragione per usare sempre const sulla destra. Sarai coerente ovunque perché le funzioni membro const devono essere dichiarate in questo modo:

int getInt() const;

1
... beh, la "buona ragione" non è poi così convincente, perché altre possibili posizioni per la "const" non significherebbero la stessa cosa. const int& getInt(); int& const getInt();
Maestro

1
@Maestro: sto suggerendo che int const& getInt();è meglio dell'equivalente, const int& getInt();mentre il int& const getInt();confronto con esso è ridondante (i riferimenti sono già costanti) sebbene legale e di solito darà un avvertimento. Comunque, const su una funzione membro cambia il thispuntatore nella funzione da Foo* consta Foo const* const.
Nick Westgate,

constsu una funzione membro non significa affatto la stessa cosa - o è void set(int)&;una sorta di riferimento a una funzione?
Davis Herring,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.