Y at-il des inconvénients à marquer toutes les variables que vous ne modifiez pas const?

Après beaucoup de recherches sur Google, j’ai trouvé beaucoup de choses sur le marquage des fonctions et de leurs parameters en tant que const , mais aucun guide sur le marquage des variables en tant que const .

Voici un exemple très simple:

 #include  #include  void example(const std::ssortingng& x) { size_t length = x.length(); for (size_t i = 0; i < length; ++i) { std::cout << x.at(i) << std::endl; } } int main() { example("hello"); } 

Pourquoi ne pas faire

 size_t length = x.length(); 

const comme

 const size_t length = x.length(); 

par convention?

Je sais qu’un exemple si petit et si simple ne montre aucun avantage énorme pour cela, mais il semble que ce serait utile dans une base de code plus grande où vous pourriez muter accidentellement une variable que vous n’auriez pas dû muter.

Malgré cet avantage, je ne le vois pas vraiment utiliser autant (dans les bases de code C ++ que j’ai vues) ou mentionné presque autant que faire des fonctions et leurs parameters const .

Y a-t-il un inconvénient à faire cela autre que d’avoir à taper 5 caractères supplémentaires? Je n’ai pas trouvé grand chose sur le sujet, et je ne veux pas me tirer dans le pied si c’est un problème avec autant de consts.

Il n’y a pas d’inconvénient à marquer les variables que vous ne modifiez pas const .

Il y a cependant des aspects positifs: le compilateur vous aidera à diagnostiquer lorsque vous modifiez involontairement une variable à laquelle vous ne voulez pas / ne voulez pas faire et le compilateur peut générer (bien que le langage const_cast et mutable soit rare) code.

Donc, je conseillerais utilisez const où vous pouvez. Il n’y a aucun inconvénient et votre compilateur peut potentiellement vous aider à repérer les bogues. Aucune raison de ne pas le faire (sauf pour un peu de frappe supplémentaire).

Notez que cela s’étend également aux fonctions membres. Faites-les const quand vous le pouvez – cela leur permet d’être utilisés dans d’autres contextes et aide les utilisateurs à raisonner sur le code (“appeler cette fonction ne modifiera pas l’object” est une information précieuse).

Je peux penser à au moins deux inconvénients:

  • verbosité: plus de mots, plus de symboles à traiter, …
  • inertie: si vous devez le modifier, vous devrez supprimer ce const

et les deux en valent la peine.


La verbosité est un argument souvent entendu contre les explications, mais les gens confondent souvent la vitesse de lecture avec la rapidité de compréhension. Il y a un équilibre à trouver entre la verbosité et le caractère explicite, certes, trop verbeux peut couvrir des informations utiles mais trop implicites / lacunaires peuvent ne pas présenter des informations à reconstruire / déduire / déduire /.

Personnellement, j’utilise un langage à vérification statique fortement typé pour que le compilateur détecte mon erreur le plus tôt possible. annoter avec const donne à la fois des informations au lecteur et au compilateur. Je juge que cela vaut les 6 symboles supplémentaires.

En ce qui concerne l’inertie, la suppression de const n’est probablement qu’un petit coût du changement … et elle se rembourse en vous obligeant à parcourir tous les endroits où elle est utilisée et à examiner le code qui l’entoure. La modification soudaine d’un élément de données particulier dans un chemin de code où il était auparavant immuable nécessite de s’assurer qu’aucune partie du chemin de code (ou de ses appelants) ne s’appuie accidentellement sur cette immuabilité.

Au lieu de ce code non standard:

 #import  #import  void example(const std::ssortingng& x) { size_t length = x.length(); for (size_t i = 0; i < length; ++i) { std::cout << x.at(i) << std::endl; } } int main() { example("hello"); } 

… J'écrirais ceci:

 #include  #include  using namespace std; void example( ssortingng const& s ) { for( char const ch : s ) { cout << ch << '\n'; } } auto main() -> int { example( "hello" ); } 

L'endroit principal que je pourrais append par rapport au code d'origine était la variable ch dans la boucle. Je pense que c'est gentil. const est généralement souhaitable car elle réduit les actions de code possibles à prendre en compte et les boucles basées sur les plages vous permettent d’avoir plus de const .

Le principal inconvénient de l'utilisation de const pour la plupart des choses est lorsque vous devez vous connecter à des API C.

Il suffit ensuite de prendre quelques décisions const_cast quant à savoir s'il faut copier des données, ou faire confiance à la documentation et utiliser un const_cast .


Addendum 1:
Notez que const sur un type de retour empêche la sémantique de déplacement. Pour autant que je sache, cela a d'abord été noté par Andrei Alexandrescu dans son article Mojo (C ++ 03 move sémantics) du Dr Dobbs Journal:

[A] const temporaire ressemble à un oxymore, une contradiction dans les termes. D'un sharepoint vue pratique, les const constantes forcent la copie à destination.

Donc, c'est un endroit où il ne faut pas utiliser const .

Désolé d'avoir oublié de le mentionner à l'origine. Le commentaire de l'utilisateur bogdan m'a rappelé une autre réponse.


Addendum 2:
Dans le même ordre d'idées (pour prendre en charge la sémantique de déplacement), si la dernière chose à faire avec un argument formel est de stocker une copie quelque part, il est préférable d'utiliser un argument non const transmis par valeur parce qu'il peut être simplement déplacé de.

C'est à dire au lieu de

 ssortingng stored_value; void foo( ssortingng const& s ) { some_action( s ); stored_value = s; } 

… Ou la redondance optimisée

 ssortingng stored_value; void foo( ssortingng const& s ) { some_action( s ); stored_value = s; } void foo( ssortingng&& s ) { some_action( s ); stored_value = move( s ); } 

… Envisagez de simplement écrire

 ssortingng stored_value; void foo( ssortingng s ) { some_action( s ); stored_value = move( s ); } 

Il peut être légèrement moins efficace dans le cas de l'argument réel de lvalue, il supprime les avantages de const (contraintes sur ce que le code pourrait faire) et rompt une convention uniforme d'utilisation de const mesure du possible dans n'importe quelle situation (qui est l'objective principal, pour éviter cela) et c'est un code plus petit et peut-être plus clair.


Notes :
¹ Le standard C ++ n'a pas de directive #import . De plus, ces en-têtes, lorsqu'ils sont correctement inclus, ne garantissent pas de définir size_t dans l'espace de noms global.

Pour la variable locale size_t length dans une méthode courte comme celle-ci, cela n’a pas vraiment d’importance. L’inconvénient de la verbosité supplémentaire s’équilibre essentiellement avec la sécurité relative d’éviter les fautes de frappe modifiant accidentellement la longueur. Faites ce que votre guide de style local, ou votre propre instinct vous dit.

Pour une méthode plus longue ou plus complexe, cela peut être différent. Mais encore une fois, si vous avez une méthode si complexe que cela compte, vous devriez peut-être au moins envisager de refactoriser votre code en des éléments plus simples … De toute façon, si vous lisez et comprenez le code, un indicateur supplémentaire explicite n’est pas pertinent – Bien mais sans importance.


Légèrement lié, bien que vous ne l’ayez pas demandé: Pour le paramètre de référence de votre méthode example , vous voulez certainement avoir const , car vous pourriez avoir besoin de lui passer une chaîne de caractères const. Seulement si vous voulez désactiver le passage de la chaîne de caractères constants (parce que vous pensez que vous allez append du code pour le modifier), vous devez omettre const .

Je sais qu’un exemple si petit et si simple ne montre aucun avantage énorme pour cela, mais il semble que ce serait utile dans une base de code plus grande où vous pourriez muter accidentellement une variable que vous n’auriez pas dû muter.

Le problème est que cela ne se produit pratiquement jamais.

D’un autre côté, la const est une maladie qui se propagera dans votre base de code comme une peste. Dès que vous déclarez une variable const , toutes les choses dont vous avez besoin doivent être const . Elles doivent donc uniquement appeler les fonctions const et elles ne s’arrêtent jamais.

const ne vaut pas le prix que vous payez dans la majorité infinie des situations. Il n’y a que quelques cas où const vous protège réellement (par exemple, définir des clés), mais même dans ce cas, il est discutable que vous deviez être un crétin total pour essayer cela, et ne vaut probablement pas toutes les règles de langage et duplication incessante du code et métalogique redondant.

const est une bonne idée qui pourrait être intéressante en théorie, mais les réalités pratiques sont que le const est une perte de temps et d’espace totale. Brûlez-le avec le feu.

Je suis d’accord avec la plupart des réponses données jusqu’ici, mais certains aspects manquent encore.

Lors de la définition des interfaces, le mot clé const est votre ami. Mais vous devez savoir que c’est aussi quelque peu limité et parfois même égoïste – c’est ce que j’appellerais ses inconvénients.

Regardons de plus près la question:

Y at-il des inconvénients à marquer toutes les variables que vous ne modifiez pas const?

Si vous observez que vous ne modifiez pas quelque chose, vous pouvez dire que c’est effectivement une constante. Les outils d’parsing de code peuvent également détecter cela, même votre compilateur le saura déjà. Mais cette observation ne devrait pas déclencher un réflexe add-const en vous.

Au lieu de penser à la variable elle-même, demandez

  • Est-ce utile du tout?
  • Est-ce aussi destiné à être constant?

Parfois, une variable intermédiaire peut simplement être supprimée, parfois une petite reprise (l’ajout ou la suppression d’une fonction) peut être effectuée pour améliorer le code.

L’ajout du mot clé const peut renforcer votre code contre les erreurs ailleurs, mais également contre les modifications au niveau du sharepoint déclaration.

Permettez-moi également d’append un mot sur les variables membres qui ne changent pas. Si vous décidez de déclarer une variable membre const , vous devez l’initialiser dans la liste d’initialisation du constructeur de la classe contenant, ce qui étend les conditions préalables à la construction d’objects de cette classe.

N’ajoutez donc pas de const là où votre compilateur le permet. Ne vous laissez pas séduire par “pésortingfier votre code” 😉