[C++] Utilisation de 'const' pour les paramètres de fonction


Answers

"const est inutile quand l'argument est passé en valeur puisque vous ne modifierez pas l'objet de l'appelant."

Faux.

Il s'agit d'auto-documenter votre code et vos hypothèses.

Si votre code a beaucoup de gens qui travaillent dessus et que vos fonctions ne sont pas triviales, alors vous devez marquer "const" tout et tout ce que vous pouvez. Lorsque vous rédigez un code de qualité industrielle, vous devez toujours supposer que vos collègues sont des psychopathes qui essaient de vous obtenir le meilleur moyen possible (surtout parce que c'est souvent vous-même dans le futur).

En outre, comme quelqu'un l'a mentionné plus tôt, cela pourrait aider le compilateur à optimiser un peu les choses (bien que ce soit long).

Question

Jusqu'où allez-vous avec const ? Est-ce que vous faites juste des fonctions de const quand c'est nécessaire ou est-ce que vous allez tout le cochon et l'utilisez partout? Par exemple, imaginez un simple mutateur qui prend un seul paramètre booléen:

void SetValue(const bool b) { my_val_ = b; }

Est-ce que const vraiment utile? Personnellement, je choisis de l'utiliser largement, y compris les paramètres, mais dans ce cas, je me demande si cela vaut la peine?

J'ai également été surpris d'apprendre que vous pouvez omettre const de paramètres dans une déclaration de fonction, mais peut l'inclure dans la définition de la fonction, par exemple:

fichier .h

void func(int n, long l);

fichier .cpp

void func(const int n, const long l)

Y a-t-il une raison à cela? Cela me semble un peu inhabituel.




There's really no reason to make a value-parameter "const" as the function can only modify a copy of the variable anyway.

The reason to use "const" is if you're passing something bigger (eg a struct with lots of members) by reference, in which case it ensures that the function can't modify it; or rather, the compiler will complain if you try to modify it in the conventional way. It prevents it from being accidentally modified.




Les paramètres de valeur de marquage 'const' sont définitivement une chose subjective.

Cependant, je préfère réellement marquer les paramètres de valeur const, tout comme dans votre exemple.

void func(const int n, const long l) { /* ... */ }

La valeur pour moi indique clairement que les valeurs des paramètres de la fonction ne sont jamais modifiées par la fonction. Ils auront la même valeur au début qu'à la fin. Pour moi, cela fait partie d'un style de programmation très fonctionnel.

Pour une fonction courte, c'est sans doute une perte de temps / espace pour avoir le 'const' là-bas, car il est généralement assez évident que les arguments ne sont pas modifiés par la fonction.

Cependant, pour une fonction plus grande, c'est une forme de documentation d'implémentation, et elle est appliquée par le compilateur.

Je peux être sûr que si je fais un calcul avec 'n' et 'l', je peux refactoriser / déplacer ce calcul sans crainte d'obtenir un résultat différent parce que j'ai raté un endroit où l'un ou les deux sont changés.

Comme il s'agit d'un détail d'implémentation, vous n'avez pas besoin de déclarer les paramètres de valeur const dans l'en-tête, tout comme vous n'avez pas besoin de déclarer les paramètres de la fonction avec les mêmes noms que l'implémentation.




Il y a une bonne discussion sur ce sujet dans les anciens articles "Guru of the Week" sur comp.lang.c ++ modérés here .

L'article correspondant de GOTW est disponible sur le site Web de Herb Sutter here .




Je ne mettrais pas const sur des paramètres comme ça - tout le monde sait déjà qu'un booléen (par opposition à un booléen &) est constant, donc l'ajouter fera penser aux gens "attendez, quoi?" ou même que vous passez le paramètre par référence.




Si vous utilisez les opérateurs ->* ou .* , C'est un must.

Cela vous empêche d'écrire quelque chose comme

void foo(Bar *p) { if (++p->*member > 0) { ... } }

ce que j'ai presque fait en ce moment, et qui ne fait probablement pas ce que vous voulez.

Ce que j'avais l'intention de dire était

void foo(Bar *p) { if (++(p->*member) > 0) { ... } }

et si j'avais mis un const entre Bar * et p , le compilateur m'aurait dit ça.




Ah, un dur. D'un côté, une déclaration est un contrat et cela n'a vraiment aucun sens de passer un argument const par valeur. D'un autre côté, si vous regardez l'implémentation de la fonction, vous donnez au compilateur plus de chances d'optimiser si vous déclarez un argument constant.




J'utilise const que je peux. Const pour les paramètres signifie qu'ils ne devraient pas changer leur valeur. Ceci est particulièrement utile lors du passage par référence. const pour la fonction déclare que la fonction ne doit pas changer les membres des classes.




Les deux lignes suivantes sont fonctionnellement équivalentes:

int foo (int a);
int foo (const int a);

Évidemment, vous ne serez pas capable de modifier a dans le corps de foo s'il est défini en deuxième, mais il n'y a pas de différence de l'extérieur.

const est vraiment utile est avec les paramètres de référence ou de pointeur:

int foo (const BigStruct &a);
int foo (const BigStruct *a);

Ce que cela signifie, c'est que foo peut prendre un grand paramètre, peut-être une structure de données de taille gigabit, sans le copier. En outre, il dit à l'appelant, "Foo ne changera pas le contenu de ce paramètre." Passer une référence const permet également au compilateur de prendre certaines décisions de performance.

*: A moins que ça ne rejette la constance, mais c'est un autre post.




Le paramètre Const n'est utile que lorsque le paramètre est transmis par référence, c'est-à-dire référence ou pointeur. Lorsque le compilateur voit un paramètre const, il s'assure que la variable utilisée dans le paramètre n'est pas modifiée dans le corps de la fonction. Pourquoi est-ce que quelqu'un voudrait faire un paramètre par valeur comme constant? :-)




Being a VB.NET programmer that needs to use a C++ program with 50+ exposed functions, and a .h file that sporadically uses the const qualifier, it is difficult to know when to access a variable using ByRef or ByVal.

Of course the program tells you by generating an exception error on the line where you made the mistake, but then you need to guess which of the 2-10 parameters is wrong.

So now I have the distasteful task of trying to convince a developer that they should really define their variables (in the .h file) in a manner that allows an automated method of creating all of the VB.NET function definitions easily. They will then smugly say, "read the ... documentation."

I have written an awk script that parses a .h file, and creates all of the Declare Function commands, but without an indicator as to which variables are R/O vs R/W, it only does half the job.

MODIFIER:

At the encouragement of another user I am adding the following;

Here is an example of a (IMO) poorly formed .h entry;

typedef int (EE_STDCALL *Do_SomethingPtr)( int smfID, const char* cursor_name, const char* sql );

The resultant VB from my script;

    Declare Function Do_Something Lib "SomeOther.DLL" (ByRef smfID As Integer, ByVal cursor_name As String, ByVal sql As String) As Integer

Note the missing "const" on the first parameter. Without it, a program (or another developer) has no Idea the 1st parameter should be passed "ByVal." By adding the "const" it makes the .h file self documenting so that developers using other languages can easily write working code.




All the consts in your examples have no purpose. C++ is pass-by-value by default, so the function gets copies of those ints and booleans. Even if the function does modify them, the caller's copy is not affected.

So I'd avoid extra consts because

  • They're redudant
  • They clutter up the text
  • They prevent me from changing the passed in value in cases where it might be useful or efficient.



Je n'utilise pas const pour les paramètres passés par la valeur. L'appelant ne se soucie pas de savoir si vous modifiez le paramètre ou non, c'est un détail d'implémentation.

Ce qui est vraiment important, c'est de marquer les méthodes comme const si elles ne modifient pas leur instance. Faites ceci comme vous allez, sinon vous pourriez vous retrouver avec beaucoup de const_cast <> ou vous pourriez trouver que marquer une méthode const nécessite de changer beaucoup de code car il appelle d'autres méthodes qui auraient dû être marquées const.

J'ai aussi tendance à marquer les variables locales const si je n'ai pas besoin de les modifier. Je crois que cela facilite la compréhension du code en facilitant l'identification des «parties mobiles».







const aurait dû être la valeur par défaut en C ++. Comme ça :

int i = 5 ; // i is a constant

var int i = 5 ; // i is a real variable