Les constantes superflues sont mauvaises du point de vue de l'API :
Placez des const superflus dans votre code pour les paramètres de type intrinsèque passés par valeur. encombre votre API tout en ne faisant aucune promesse significative à l'appelant ou à l'utilisateur de l'API (cela ne fait que gêner la mise en œuvre).
Trop de 'const' dans une API lorsqu'ils ne sont pas nécessaires, c'est comme " crier au loup ", les gens finiront par ignorer le terme " const " parce qu'il est partout et ne signifie rien la plupart du temps.
L'argument "reductio ad absurdum" pour que les constantes supplémentaires dans l'API soient bonnes pour ces deux premiers points serait que si plus de paramètres const sont bons, alors chaque argument qui peut avoir une const, DEVRAIT avoir une const. En fait, si c'était vraiment si bon, vous voudriez que const soit la valeur par défaut pour les paramètres et avoir un mot clé comme "mutable" uniquement lorsque vous voulez changer le paramètre.
Alors essayons de mettre des constantes partout où nous le pouvons :
void mungerum(char * buffer, const char * mask, int count);
void mungerum(char * const buffer, const char * const mask, const int count);
Considérez la ligne de code ci-dessus. Non seulement la déclaration est plus encombrée, plus longue et plus difficile à lire, mais trois des quatre mots-clés "const" peuvent être ignorés sans risque par l'utilisateur de l'API. Cependant, l'utilisation supplémentaire de "const" a rendu la deuxième ligne potentiellement plus complexe. DANGEREUX !
Pourquoi ?
Une rapide erreur de lecture du premier paramètre char * const buffer
pourrait vous faire penser qu'il ne modifiera pas la mémoire dans le tampon de données qui lui est passé - cependant, ce n'est pas vrai ! Les "const" superflus peuvent conduire à des hypothèses dangereuses et incorrectes concernant votre API. lorsqu'ils sont scannés ou mal lus rapidement.
Les constantes superflues sont également mauvaises du point de vue de la mise en œuvre du code :
#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);
Si FLEXIBLE_IMPLEMENTATION n'est pas vrai, alors l'API "promet" de ne pas implémenter la fonction de la première façon ci-dessous.
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];
}
}
C'est une promesse très stupide à faire. Pourquoi faire une promesse qui n'apporte aucun avantage à votre interlocuteur et ne fait que limiter votre mise en œuvre ?
Les deux sont des implémentations parfaitement valides de la même fonction, donc vous n'avez fait que vous attacher une main derrière le dos inutilement.
En outre, il s'agit d'une promesse très superficielle qui est facilement (et légalement) contournée.
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);
}
Regardez, je l'ai implémenté de cette façon de toute façon, même si j'ai promis de ne pas le faire - juste en utilisant une fonction d'enveloppe. C'est comme lorsque le méchant promet de ne pas tuer quelqu'un dans un film et ordonne à ses sbires de le faire à sa place.
Ces constantes superflues ne valent pas plus qu'une promesse d'un méchant de cinéma.
Mais la capacité à mentir est encore pire :
J'ai appris qu'il est possible de ne pas faire correspondre les const dans l'en-tête (déclaration) et le code (définition) en utilisant des const fallacieux. Les défenseurs de la const-happy prétendent que c'est une bonne chose puisque cela vous permet de mettre const uniquement dans la définition.
// Example of const only in definition, not declaration
struct foo { void test(int *pi); };
void foo::test(int * const pi) { }
Cependant, l'inverse est vrai... vous pouvez mettre un const fallacieux uniquement dans la déclaration et l'ignorer dans la définition. Cela ne fait que rendre les const superflus dans une API plus une chose terrible et un mensonge horrible - voir cet exemple :
struct 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
}
Tout ce que fait le const superflu est de rendre le code de l'implémenteur moins lisible en le forçant à utiliser une autre copie locale ou une fonction wrapper lorsqu'il veut changer la variable ou passer la variable par référence non const.
Regardez cet exemple. Lequel est le plus lisible ? Est-il évident que la seule raison de la variable supplémentaire dans la deuxième fonction est qu'un concepteur d'API a ajouté une constante superflue ?
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;
}
}
Espérons que nous avons appris quelque chose ici. Les const superflus sont une pollution visuelle de l'API, une nuisance agaçante, une promesse superficielle et dénuée de sens, une entrave inutile, et conduisent parfois à des erreurs très dangereuses.
0 votes
Je ne suis pas d'accord. Le fichier .h doit également contenir les définitions des constantes. Si ce n'est pas le cas, si des paramètres constants sont passés à la fonction, le compilateur générera une erreur, car le prototype dans le fichier .h n'a pas les définitions constantes.
15 votes
Je suis d'accord :-) (Avec la question, pas le dernier commentaire !) Si une valeur ne doit pas être changée dans le corps de la fonction, cela peut aider à arrêter les bugs stupides de == ou =, vous ne devriez jamais mettre const dans les deux, (si c'est passé par valeur, vous devez autrement) Ce n'est pas assez sérieux pour entrer dans des arguments à ce sujet cependant !
27 votes
@selwyn : Même si vous passez un const int à la fonction, cependant, il va être copié (puisque ce n'est pas une référence), et donc le const-ness n'a pas d'importance.
1 votes
Le même débat se déroule dans cette question : stackoverflow.com/questions/1554750/
7 votes
Je réalise que ce message date de quelques années, mais en tant que nouveau programmeur, je me posais cette question et je suis tombé sur cette conversation. A mon avis, si une fonction ne doit pas modifier une valeur, qu'il s'agisse d'une référence ou d'une copie de la valeur/objet, elle doit être const. C'est plus sûr, c'est auto-documenté, et c'est plus facile à déboguer. Même pour la fonction la plus simple, qui n'a qu'une seule instruction, j'utilise toujours const.
0 votes
Maintenant, avec la sémantique des déplacements, l'utilisation de const pourrait avoir un coût en termes de performances.