51 votes

Y a-t-il des inconvénients à marquer toutes les variables que vous ne modifiez pas en constantes ?

Après avoir beaucoup cherché sur Google, j'ai trouvé beaucoup d'informations sur le marquage des fonctions et de leurs paramètres comme suit const mais aucun guide sur le marquage des variables comme const .

Voici un exemple très simple :

#include <string>
#include <iostream>

void example(const std::string& 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();

comme

const size_t length = x.length();

par convention ?

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

Malgré cet avantage, je ne le vois pas vraiment utilisé (dans les bases de code C++ que j'ai vues) ou mentionné aussi souvent que la création de fonctions et de leurs paramètres. const .

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

41voto

Jesper Juhl Points 14756

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

Il y a cependant quelques avantages : le compilateur vous aidera à diagnostiquer les cas où vous modifiez involontairement une variable que vous ne devriez pas/ne vouliez pas modifier et le compilateur mai (bien qu'en raison de la langue ayant const_cast y mutable c'est rare) génèrent un meilleur code.

Donc, je conseille d'utiliser const où vous pouvez. Il n'y a pas d'inconvénients et votre compilateur peut potentiellement vous aider à repérer les bogues. Il n'y a aucune raison de ne pas le faire (à l'exception d'un peu de saisie supplémentaire).

Notez que cela s'applique également aux fonctions membres. Faites-les const quand vous le pouvez - cela leur permet d'être utilisés dans plus de contextes et aide les utilisateurs à raisonner sur le code ("appeler cette fonction ne modifiera pas l'objet" est une information précieuse).

19voto

Matthieu M. Points 101624

Je peux penser à au moins deux inconvénients :

  • verbosité : plus de mots, plus de symboles à traiter, ...
  • inertie : si vous devez le modifier, vous devrez aller retirer ce document. const

et les deux en valent la peine.


La verbosité est un argument souvent entendu contre l'explicitation, mais les gens confondent souvent vitesse de lecture et vitesse de compréhension. Il y a un équilibre à trouver entre verbosité et explicitation, certes, trop verbeux peut noyer l'information utile mais trop implicite/terse peut ne pas présenter l'information qui doit être reconstruite/inférée/éduquée/.

Personnellement, j'utilise un langage fortement typé et contrôlé statiquement afin que le compilateur détecte mon erreur le plus tôt possible ; j'annote avec const c'est à la fois donner des informations au lecteur et le compilateur. J'estime que cela vaut la peine d'utiliser les 6 symboles supplémentaires.

Quant à l'inertie, le fait d'enlever const n'est probablement qu'un coût minime de la modification... et il se rembourse en vous obligeant à passer en revue tous les endroits où il est utilisé et à revoir le code environnant pour vous assurer qu'il est vraiment correct de supprimer ce const . Pour modifier soudainement un élément de données particulier dans un chemin de code où il était auparavant immuable, il faut s'assurer qu'aucune partie du chemin de code (ou ses appelants) ne s'est accidentellement appuyée sur cette immuabilité.

7voto

Au lieu de ce code "non standard" :

#import <string>
#import <iostream>

void example(const std::string& 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 ça :

#include <string>
#include <iostream>
using namespace std;

void example( string const& s )
{
    for( char const ch : s )
    {
        cout << ch << '\n';
    }
}

auto main()
    -> int
{ example( "hello" ); }

Le principal endroit où je pourrais ajouter const par rapport au code d'origine, était pour les ch dans la boucle. Je pense que c'est bien. const est généralement souhaitable parce qu'elle réduit le nombre d'actions de code possibles à prendre en compte, et les boucles basées sur l'intervalle vous permettent d'avoir plus d'actions de code. const .

Le principal inconvénient de l'utilisation const pour la plupart des choses, c'est lorsque vous devez vous rapporter aux API C.

Il ne reste plus qu'à prendre des décisions à l'instinct pour savoir s'il faut copier les données, ou faire confiance à la documentation et utiliser un logiciel de gestion des données. const_cast .


         Addendum 1 :
Notez bien que const sur un type de retour empêche la sémantique du déplacement. Pour autant que je sache, cela a été noté pour la première fois par Andrei Alexandrescu dans son article intitulé Article Mojo (sémantique des mouvements C++03) dans le Journal du Dr Dobbs :

" [A] const temporaire ressemble à un oxymoron, une contradiction dans les termes. D'un point de vue pratique, const les temporaires forcent la copie à la destination.

Donc, c'est un endroit où l'on devrait no utiliser const .

Désolé d'avoir oublié de mentionner ce point à l'origine ; j'ai été rappelé par Commentaire de l'utilisateur bogdan sur une autre réponse.


         Addendum 2 :
Dans la même veine (pour supporter la sémantique du déplacement), si la dernière chose faite avec un argument formel est de stocker une copie quelque part, alors au lieu de passer par référence à const il peut être préférable d'utiliser un non const l'argument passé par valeur, car il peut être simplement déplacé de.

C'est-à-dire qu'au lieu de

string stored_value;

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

ou la redondance de l'optimisé

string stored_value;

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

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

envisager d'écrire simplement

string stored_value;

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

Elle peut être légèrement moins efficace dans le cas d'un argument réel de valeur l, mais elle perd les avantages de const (contraintes sur ce que le code peut éventuellement faire), et il rompt une convention uniforme d'utilisation de l'option const dans la mesure du possible, mais il ne donne pas de mauvais résultats dans toutes les situations (ce qui est le but principal, éviter cela) et c'est un code plus petit et peut-être plus clair.


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

5voto

hyde Points 13720

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 fondamentalement avec la sécurité relative d'éviter les fautes de frappe en modifiant accidentellement la longueur. Faites ce que vous dit votre guide de style local ou votre propre intuition.

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 a de l'importance, peut-être devriez-vous au moins envisager de refactorer votre code en morceaux plus simples... Quoi qu'il en soit, si vous lisez et comprenez le code, l'indice supplémentaire fourni par l'explicite const n'est pas très pertinent - gentil mais non pertinent.


Légèrement lié, bien que vous ne l'ayez pas demandé : Pour le paramètre de référence de votre example la méthode, vous voulez certainement 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 const string (parce que vous pensez que vous ajouterez du code pour le modifier), vous devriez omettre const là.

4voto

Puppy Points 90818

Je sais qu'un exemple aussi petit et simple ne montre pas vraiment d'énorme avantages, mais il semble que ce serait utile dans une plus grande codebase où vous pourriez accidentellement muter une variable que vous ne devriez pas avoir muté.

Le problème, c'est que cela n'arrive pratiquement jamais.

D'un autre côté, const est une maladie qui sera se répandre dans votre base de code comme une peste. Dès que vous déclarez un const variable, toutes les choses dont vous avez besoin sur elle doivent être const et ils doivent donc uniquement appeler const fonctions, et ça ne s'arrête jamais.

const ne vaut pas le prix que vous payez pour elle dans l'infinie majorité des situations. Il n'y a que quelques cas où const vous protège réellement (par ex. set keys), mais même dans ce cas, on peut se demander s'il faut être totalement idiot pour essayer cela en premier lieu, et cela ne vaut probablement pas toutes les règles du langage et la duplication incessante du code et la métalogie redondante.

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X