Const avant ou const après?

146

Pour commencer, vous savez probablement que cela constpeut être utilisé pour rendre les données d'un objet ou un pointeur non modifiables ou les deux.

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

Cependant, vous pouvez également utiliser la syntaxe:

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

La seule chose qui semble avoir de l'importance, c'est de quel côté de l'astérisque vous mettez le constmot - clé. Personnellement, je préfère mettre constà gauche du type pour spécifier que les données ne sont pas modifiables car je trouve qu'elles se lisent mieux dans mon état d'esprit de gauche à droite, mais quelle syntaxe est venue en premier?

Plus important encore, pourquoi y a-t-il deux façons correctes de spécifier les constdonnées et dans quelle situation préféreriez-vous ou auriez-vous besoin de l'une par rapport à l'autre, le cas échéant?

Éditer:

Il semble donc que ce soit une décision arbitraire lorsque la norme sur la façon dont les compilateurs doivent interpréter les choses a été rédigée bien avant ma naissance. Depuis constest appliqué à ce qui est à gauche du mot-clé (par défaut?) Je suppose qu'ils ont pensé qu'il n'y avait aucun mal à ajouter des "raccourcis" pour appliquer des mots-clés et des qualificatifs de type d'une autre manière au moins jusqu'à ce que la déclaration change par analyse d'un * ou & ...

C'était aussi le cas en C alors je suppose?

AJG85
la source
9
Dans les macros, ajoutez toujours const après le type, par exemple #define MAKE_CONST(T) T constau lieu de #define MAKE_CONST(T) const Tpour que cela MAKE_CONST(int *)se développe correctement vers int * constau lieu de const int *.
jotik
7
J'ai vu ces deux styles appelés «const est» et «const ouest».
Tom Anderson
15
@TomAnderson mais en fait, il devrait être "east const" et "const west".
YSC

Réponses:

96

Pourquoi y a-t-il deux façons correctes de spécifier les constdonnées et dans quelle situation préférez-vous ou avez-vous besoin de l'une par rapport à l'autre, le cas échéant?

Essentiellement, la raison pour laquelle la position des constspécificateurs à l'intérieur avant un astérisque n'a pas d'importance est que la grammaire C a été définie de cette façon par Kernighan et Ritchie.

La raison pour laquelle ils ont défini la grammaire de cette manière était probablement que leur compilateur C analysait l'entrée de gauche à droite et avait fini de traiter chaque jeton au fur et à mesure qu'il consommait cela. La consommation du *jeton change l'état de la déclaration actuelle en un type de pointeur. Rencontre constaprès* signifie que le constqualificatif est appliqué à une déclaration de pointeur; le rencontrer avant le *moyen auquel le qualificatif est appliqué aux données pointées.

Parce que la signification sémantique ne change pas si le const qualificatif apparaît avant ou après les spécificateurs de type, il est accepté dans les deux cas.

Un cas similaire se produit lors de la déclaration de pointeurs de fonction, où:

  • void * function1(void)déclare une fonction qui retourne void *,

  • void (* function2)(void)déclare un pointeur de fonction vers une fonction qui renvoie void.

Encore une fois, la chose à noter est que la syntaxe du langage prend en charge un analyseur de gauche à droite.

Heath Hunnicutt
la source
7
Kernighan a co-écrit le livre mais n'a pas été impliqué dans la conception de C, juste Ritchie.
Tom Zych
8
Je n'ai jamais pu me rappeler lequel est lequel. Grâce à votre explication j'ai enfin une mnémotechnique pour m'en souvenir. Merci! Avant que l' *analyseur du compilateur ne sache qu'il s'agit d'un pointeur, il est donc constant pour la valeur des données. Après *, il est lié au pointeur constant. Brillant. Et enfin ça explique pourquoi je peux faire const charaussi bien que char const.
Vit Bernatik
2
La supposition quant à la raison pour laquelle cela a été fait de cette manière me semble plutôt faible / contradictoire. Autrement dit, si je définissais un langage et écrivais un compilateur, et que je voulais le garder simple et "analyser l'entrée de gauche à droite et terminer le traitement de chaque jeton au fur et à mesure qu'il le consomme", comme vous le dites, il me semble J'exigerais que le produit constvienne toujours après la chose qu'il qualifie ... exactement pour que je puisse toujours terminer le traitement du const immédiatement après l'avoir consommé. Cela semble donc être un argument pour interdire West-Const, plutôt que de l'autoriser.
Don Hatch
3
"Parce que la signification sémantique ne change pas si le qualificatif const apparaît avant ou après les spécificateurs de type, il est accepté de toute façon." N'est-ce pas un raisonnement circulaire? La question est de savoir pourquoi le sens sémantique est défini comme ça, donc je ne pense pas que cette phrase apporte quoi que ce soit.
Don Hatch
1
@donhatch Vous devez vous rappeler que, par rapport à aujourd'hui et aux hypothèses que nous faisons sur la base de notre familiarité avec la bonne conception de langage de programmation, les langages étaient des choses assez nouvelles à l'époque. Aussi, que l'on ait un langage permissif ou restreint est un jugement de valeur. Par exemple, python devrait-il avoir un ++opérateur? "La phrase", à mon humble avis, m'a aidé à réaliser qu'il n'y avait aucune raison particulière autre que parce qu'ils le pouvaient. Peut-être qu'ils feraient un choix différent aujourd'hui / peut-être pas.
ragerdl
75

La règle est:

const s'applique à la chose qui en reste. S'il n'y a rien à gauche, cela s'applique à la chose à droite.

Je préfère utiliser const à droite de la chose pour être const juste parce que c'est la manière «originale» de définir const.

Mais je pense que c'est un point de vue très subjectif.

horstforst
la source
18
Je préfère le mettre à gauche, mais je pense que le mettre à droite a plus de sens. Vous lisez généralement les types en C ++ de droite à gauche, par exemple Object const *un pointeur vers un objet const. Si vous mettez le constsur la gauche, il se lirait comme un pointeur vers un objet qui est const, qui ne coule pas vraiment très bien.
Collin Dauphinee
1
J'ai l'impression que sur la gauche, c'est pour la cohérence de style humain avec d'autres types de déclarations C (du point de vue informatique, ce n'est pas correct car ce constn'est pas une classe de stockage, mais les gens ne sont pas des analyseurs).
geekosaur
1
@Heath Je pense que c'est plus une ligne directrice qu'une règle et je l'ai souvent entendue pour me souvenir de la façon dont le compilateur l'interprétera ... Je comprends comment cela fonctionne donc j'étais seulement curieux de savoir le processus de réflexion derrière le décision de le soutenir dans les deux sens.
AJG85
3
@HeathHunnicutt la règle existe, mais c'est juste un peu plus compliqué: c-faq.com/decl/spiral.anderson.html
imallett
2
@HeathHunnicutt: la règle de la spirale est la version étendue du commentaire du premier commentateur "Vous lisez généralement les types en [C /] C ++ de droite à gauche". Je présumais que vous contredisiez cela. Cependant, je pense que vous avez plutôt fait allusion à la réponse elle-même.
imallett
57

Je préfère la deuxième syntaxe. Cela m'aide à garder une trace de `` quoi '' est constant en lisant la déclaration de type de droite à gauche:

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
la source
3
Exactement. Un "pointeur constant vers un objet constant" ne l'est Object const* constpas const const Object*. "const" ne peut pas être à gauche sauf dans le cas particulier où tant de gens l'aiment absolument. (Voir Heath ci-dessus.)
cdunn2001
38

L'ordre des mots-clés dans une déclaration n'est pas tout à fait fixe. Il existe de nombreuses alternatives au «seul vrai ordre». Comme ça

int long const long unsigned volatile i = 0;

ou devrait-il être

volatile unsigned long long int const i = 0;

??

Bo Persson
la source
25
+1 pour une définition totalement déroutante d'une variable simple. :)
Xeo
@rubenvb - Oui, malheureusement, ils le sont. La grammaire dit simplement que a decl-specifier-seqest une séquence de decl-specifiers. Il n'y a pas d'ordre donné par la grammaire, et le nombre d'occurrences pour chaque mot-clé n'est limité que par quelques règles sémantiques (vous pouvez en avoir un constmais deux long:-)
Bo Persson
3
@rubenvb - Oui, unsignedest un type, le même que unsigned intet int unsigned. unsigned longest un autre type, le même que unsigned long intet int long unsigned. Vous voyez le modèle?
Bo Persson
2
@Bo: Je vois le désordre, je dois en avoir trois pour voir un modèle ;). OK, merci
rubenvb
1
Vous aviez l'habitude d'ajouter staticau fouillis de mots, mais ce n'est que récemment que les compilateurs se sont plaints du fait qu'il staticfallait passer en premier.
Mark Lakata
8

La première règle consiste à utiliser le format requis par vos normes de codage locales. Après cela: mettre le constdevant ne mène à aucune confusion lorsque des typedefs sont impliqués, par exemple:

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

Si votre standard de codage autorise les typedef de pointeurs, alors il devrait vraiment insister pour mettre le const après le type. Dans tous les cas, sauf lorsqu'il est appliqué au type, const doit suivre ce à quoi il s'applique, donc la cohérence plaide également en faveur de la const après. Mais les directives de codage locales l'emportent sur tous ces éléments; la différence n'est normalement pas assez importante pour revenir en arrière et modifier tout le code existant.

James Kanze
la source
Je pense que cela peut mettre en évidence la raison pour laquelle nous n'avons pas de typedefs de pointeurs dans nos normes plutôt vaguement définies dans cette boutique.
AJG85
1
Ma politique (quand je décide seul) est de mettre le const après (par souci de cohérence) et de ne pas utiliser de typedefs vers des pointeurs (ou des typedefs en général) :-). Et BTW, string :: iterator vs string :: const_iterator devrait probablement être également pris en compte dans votre décision. (Juste pour confondre les choses :-). Il n'y a pas de bonne réponse.)
James Kanze
Ah oui j'aurais pu inclure le comportement de const std::string::const_iteratoraussi bien pour faire bonne mesure;)
AJG85
2
@JamesKanze - Attendez une minute, aidez-moi ici ... Je ne vois pas la confusion dans l'exemple posté. Que pourrait const IntPtr p1éventuellement signifier autre que "pointeur entier constant" (c'est-à-dire "pointeur constant vers entier")? Personne dans son bon sens, même sans savoir comment IntPtrest défini, ne penserait que p1c'est mutable. Et d'ailleurs, pourquoi quelqu'un supposerait-il à tort que *p1c'est immuable? De plus, mettre le const ailleurs (par exemple IntPtr const p1) ne change pas du tout la sémantique.
Todd Lehman
3
@ToddLehman Vous ne voyez peut-être pas la confusion, mais la plupart des programmeurs C ++ le font, et se trompent systématiquement (sans doute aidés par des choses comme std::vector<T>::const_iterator, où ce n'est pas l'itérateur qui est const, mais vers quoi il pointe).
James Kanze
7

Il y a des raisons historiques pour lesquelles la gauche ou la droite est acceptable. Stroustrup avait ajouté const au C ++ en 1983 , mais il ne l'a fait en C qu'en C89 / C90.

En C ++, il y a une bonne raison de toujours utiliser const sur la droite. Vous serez cohérent partout car les fonctions membres const doivent être déclarées de cette façon:

int getInt() const;
Nick Westgate
la source
1
... eh bien, la "bonne raison" n'est pas très convaincante, car d'autres emplacements possibles pour le "const" ne signifieraient pas la même chose. const int& getInt(); int& const getInt();
Maestro
1
@Maestro: Je suggère que int const& getInt();c'est mieux que l'équivalent const int& getInt();alors int& const getInt();que le comparer avec est redondant (les références sont déjà const) bien que légal et donnera généralement un avertissement. Quoi qu'il en soit, const sur une fonction membre change le thispointeur de la fonction de Foo* constà Foo const* const.
Nick Westgate
constsur une fonction membre ne signifie pas du tout la même chose - ou est-ce void set(int)&;une sorte de référence à une fonction?
Davis Herring