c++ - Utilisation de 'const' pour les paramètres de fonction


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.




Answers


La raison est que const pour le paramètre s'applique uniquement localement dans la fonction, car il travaille sur une copie des données. Cela signifie que la signature de la fonction est vraiment la même de toute façon. C'est probablement un mauvais style de faire ça beaucoup.

Personnellement, j'ai tendance à ne pas utiliser const sauf pour les paramètres de référence et de pointeur. Pour les objets copiés, cela n'a pas vraiment d'importance, bien que cela puisse être plus sûr car cela signale l'intention dans la fonction. C'est vraiment un appel de jugement. J'ai tendance à utiliser const_iterator quand je fais une boucle sur quelque chose et je n'ai pas l'intention de le modifier, donc je suppose à chacun son propre, aussi longtemps que const correct pour les types de référence est rigoureusement maintenu.




"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 écrivez 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).




Parfois (trop souvent!) Je dois démêler le code C ++ de quelqu'un d'autre. Et nous savons tous que le code C ++ de quelqu'un d'autre est un désordre complet presque par définition :) Donc la première chose que je fais pour déchiffrer le flux de données local est de mettre const dans toutes les variables jusqu'à ce que le compilateur commence à aboyer. Cela signifie également des arguments de valeur const-qualificatifs, car ce ne sont que des variables locales fantaisie initialisées par l'appelant.

Ah, j'aimerais que les variables soient const par défaut et mutable pour les variables non-const :)




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 est que foo peut prendre un grand paramètre, peut-être une structure de données de taille gigaoctets, 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.




Les constants Superfluous supplémentaires sont mauvais du point de vue de l'API:

Mettre des consts superflus supplémentaires dans votre code pour les paramètres de type intrinsèque transmis par valeur encombre votre API sans faire de promesses significatives à l'appelant ou à l'utilisateur de l'API (cela ne fait que gêner l'implémentation).

Trop de 'const' dans une API quand ce n'est pas nécessaire est comme " pleurer le loup ", finalement les gens vont commencer à ignorer 'const' parce que c'est partout et ne veut rien dire la plupart du temps.

L'argument "reductio ad absurdum" pour des consts supplémentaires dans l'API est bon car ces deux premiers points seraient si plus de paramètres const sont bons, alors chaque argument qui peut avoir un const sur lui, DEVRAIT avoir un const sur lui. En fait, si c'était vraiment bon, vous voudriez que const soit la valeur par défaut pour les paramètres et avoir un mot-clé comme "mutable" seulement quand vous voulez changer le paramètre.

Alors essayons de mettre const là où nous 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 et plus longue et plus difficile à lire, mais trois des quatre mots clés «const» peuvent être ignorés en toute sécurité par l'utilisateur de l'API. Cependant, l'utilisation de 'const' a rendu la seconde ligne potentiellement dangereuse!

Pourquoi?

Une mauvaise lecture rapide 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 est passé - cependant, ce n'est pas vrai! Superflue 'const' peut conduire à des hypothèses dangereuses et incorrectes au sujet de votre API lors de la numérisation ou mal lu rapidement.

Les consts superflus sont également mauvais du point de vue de l'implémentation 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, l'API est "prometteuse" de ne pas implémenter la fonction comme indiqué 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 devriez-vous faire une promesse qui ne donne aucun avantage à votre interlocuteur et limite seulement votre implémentation?

Les deux sont des implémentations parfaitement valables de la même fonction, donc tout ce que vous avez fait est lié une main derrière votre dos inutilement.

De plus, c'est 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 mis en œuvre de cette façon, même si j'ai promis de ne pas - juste en utilisant une fonction d'emballage. C'est comme quand le méchant promet de ne pas tuer quelqu'un dans un film et ordonne à son acolyte de les tuer à la place.

Ces consts superflus ne valent rien de plus qu'une promesse d'un méchant de cinéma.

Mais la capacité de mentir devient encore pire:

J'ai été éclairé que vous pouvez confondre const dans l'en-tête (déclaration) et le code (définition) en utilisant const constable. Les avocats const-happy affirment que c'est une bonne chose car cela vous permet de mettre const uniquement dans la définition.

// Example of const only in definition, not declaration
class foo { void test(int *pi); };
void foo::test(int * const pi) { }

Cependant, l'inverse est vrai ... vous ne pouvez mettre un const inexact que dans la déclaration et l'ignorer dans la définition. Cela ne fait que rendre superflu un const dans une API plus terrible et un mensonge horrible - voir cet exemple:

class 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 qui est 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 quand il veut changer la variable ou passer la variable par une 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 parce que certains concepteurs d'API ont jeté un const superflu?

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;
    }
}

J'espère que nous avons appris quelque chose ici. Superflue const est une horreur encombrante API, un harceler ennuyeux, une promesse superficielle et sans signification, un obstacle inutile, et conduit parfois à des erreurs très dangereuses.




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



Quand j'ai codé le C ++ pour gagner ma vie, j'ai tout arrangé. L'utilisation de const est une excellente façon d'aider le compilateur à vous aider. Par exemple, la constation des valeurs de retour de votre méthode peut vous éviter des fautes de frappe telles que:

foo() = 42

quand tu as voulu dire:

foo() == 42

Si foo () est défini pour renvoyer une référence non-const:

int& foo() { /* ... */ }

Le compilateur vous permettra heureusement d'attribuer une valeur à l'attribut temporaire anonyme renvoyé par l'appel de la fonction. Faire const:

const int& foo() { /* ... */ }

Élimine cette possibilité.




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

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




J'utilise const sur les paramètres de la fonction qui sont des références (ou des pointeurs) qui ne sont que [dans] les données et ne seront pas modifiés par la fonction. C'est-à-dire lorsque le but de l'utilisation d'une référence est d'éviter de copier des données et de ne pas autoriser la modification du paramètre transmis.

Mettre const dans le paramètre boolean b dans votre exemple ne fait que mettre une contrainte sur l'implémentation et ne contribue pas à l'interface de la classe (bien que le fait de ne pas changer de paramètre soit généralement conseillé).

La signature de la fonction pour

void foo(int a);

et

void foo(const int a);

est le même, ce qui explique votre .c et .h

Asaf




Je dis const vos paramètres de valeur.

Considérez cette fonction buggy:

bool isZero(int number)
{
  if (number = 0)  // whoops, should be number == 0
    return true;
  else
    return false;
}

Si le paramètre number était const, le compilateur s'arrêterait et nous avertirait du bug.




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 plus de chances au compilateur d'optimiser si vous déclarez un argument constant.




const est inutile lorsque l'argument est passé en valeur puisque vous ne modifiez pas l'objet de l'appelant.

const devrait être préféré lors du passage par référence, sauf si le but de la fonction est de modifier la valeur passée.

Enfin, une fonction qui ne modifie pas l'objet courant (this) peut, et devrait probablement être déclarée const. Un exemple est ci-dessous:

int SomeClass::GetValue() const {return m_internalValue;}

Ceci est une promesse de ne pas modifier l'objet auquel cet appel est appliqué. En d'autres termes, vous pouvez appeler:

const SomeClass* pSomeClass;
pSomeClass->GetValue();

Si la fonction n'était pas const, cela entraînerait un avertissement du compilateur.




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.




Dans le cas que vous mentionnez, cela n'affecte pas les appelants de votre API, ce qui explique pourquoi ce n'est pas souvent fait (et n'est pas nécessaire dans l'en-tête). Cela n'affecte que la mise en œuvre de votre fonction.

Ce n'est pas particulièrement une mauvaise chose à faire, mais les avantages ne sont pas si grands que cela n'affecte pas votre API, et il ajoute la frappe, donc ce n'est généralement pas fait.




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 cela appelle d'autres méthodes qui auraient dû être marquées const.

J'ai aussi tendance à marquer les const locales vars 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».




J'ai tendance à utiliser const autant que possible. (Ou tout autre mot-clé approprié pour la langue cible.) Je le fais simplement parce qu'il permet au compilateur de faire des optimisations supplémentaires qu'il ne pourrait pas faire autrement. Comme je n'ai aucune idée de ce que peuvent être ces optimisations, je le fais toujours, même là où ça me semble idiot.

Pour autant que je sache, le compilateur peut très bien voir un paramètre de valeur const, et dire, "Hé, cette fonction ne le modifie pas de toute façon, donc je peux passer par référence et enregistrer des cycles d'horloge." Je ne pense pas qu'il ferait jamais une telle chose, car il change la signature de la fonction, mais il fait le point. Peut-être qu'il fait une manipulation de pile différente ou quelque chose ... Le point est, je ne sais pas, mais je sais essayer d'être plus intelligent que le compilateur ne conduit qu'à me faire honte.

C ++ a quelques bagages supplémentaires, avec l'idée de const-justesse, donc cela devient encore plus important.




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.




Peut être ceci ne sera pas un argument valide. mais si l'on incrémente la valeur d'une variable const dans un compilateur de fonction, on obtiendra une erreur: " error: increment du paramètre read-only ". Cela signifie que nous pouvons utiliser le mot clé const pour éviter de modifier accidentellement nos variables à l'intérieur des fonctions (ce que nous ne sommes pas supposés / en lecture seule). Donc, si nous l'avons accidentellement fait au moment de la compilation, le compilateur nous le fera savoir. Ceci est particulièrement important si vous n'êtes pas le seul à travailler sur ce projet.




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? :-)




Si le paramètre est passé par value (et n'est pas une référence), il n'y a généralement pas de différence si le paramètre est déclaré const ou non (à moins qu'il ne contienne un membre de référence - pas un problème pour les types intégrés). Si le paramètre est une référence ou un pointeur, il est généralement préférable de protéger la mémoire référencée / pointée, pas le pointeur lui-même (je pense que vous ne pouvez pas faire la référence elle-même, pas que cela ne compte pas) . Cela semble une bonne idée de protéger tout ce que vous pouvez en tant que const. Vous pouvez l'omettre sans crainte de faire une erreur si les paramètres ne sont que des POD (y compris les types intégrés) et qu'il n'y a aucune chance qu'ils changent plus loin sur la route (par exemple dans votre exemple le paramètre bool).

Je ne connaissais pas la différence de déclaration de fichier .h / .cpp, mais cela a du sens. Au niveau du code machine, rien n'est "const", donc si vous déclarez une fonction (dans le .h) comme non-const, le code est le même que si vous le déclarez const (optimisations de côté). Cependant, il vous aide à enrôler le compilateur que vous ne changerez pas la valeur de la variable dans l'implémentation de la fonction (.ccp). Il peut s'avérer utile dans le cas où vous héritez d'une interface qui permet le changement, mais vous n'avez pas besoin de modifier le paramètre pour obtenir les fonctionnalités requises.







Résumer:

  • "Normalement, la valeur de passage de const est inutile et trompeuse au mieux." De GOTW006
  • Mais vous pouvez les ajouter dans le .cpp comme vous le feriez avec des variables.
  • Notez que la bibliothèque standard n'utilise pas const. Par exemple std::vector::at(size_type pos) . Ce qui est assez bon pour la bibliothèque standard est bon pour moi.



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.




la chose à retenir avec const est qu'il est beaucoup plus facile de faire des choses const dès le départ, que d'essayer de les mettre plus tard.

Utilisez const quand vous voulez que quelque chose soit inchangé - c'est un indice supplémentaire qui décrit ce que fait votre fonction et à quoi vous attendre. J'ai vu beaucoup d'API C qui pourraient faire avec certains d'entre eux, en particulier ceux qui acceptent les chaînes de caractères!

Je serais plus enclin à omettre le mot-clé const dans le fichier cpp que l'en-tête, mais comme j'ai tendance à couper + coller, ils seraient conservés aux deux endroits. Je ne sais pas pourquoi le compilateur le permet, je suppose que c'est un truc de compilateur. La meilleure pratique est certainement de mettre votre mot clé const dans les deux fichiers.




Il n'y a vraiment aucune raison de faire un paramètre de valeur "const" car la fonction ne peut que modifier une copie de la variable.

La raison d'utiliser "const" est si vous passez quelque chose de plus grand (par exemple une structure avec beaucoup de membres) par référence, auquel cas il s'assure que la fonction ne peut pas le modifier; ou plutôt, le compilateur se plaindra si vous essayez de le modifier de manière conventionnelle. Cela l'empêche d'être accidentellement modifié.




Comme les paramètres sont passés par valeur, il ne marche pas fait aucune différence si vous spécifiez const ou non de la fonction d'appel de perspective.It ne fondamentalement pas de sens de déclarer passe par des paramètres de valeur comme const.




Tous les consts dans vos exemples ont pas de but. C ++ est passe par la valeur par défaut, la fonction obtient des copies de ces ints et booléens. Même si la fonction ne les modifie, la copie de l'appelant n'est pas affectée.

Donc, il faut éviter consts supplémentaire parce que

  • Ils sont redudant
  • Ils encombrent le texte
  • Ils me empêchent de changer le passé en valeur dans les cas où il pourrait être utile ou efficace.



Je sais que la question est « un peu » pas à jour mais comme je suis tombé sur quelqu'un d'autre, il peut aussi le faire à l'avenir ... ... je doute encore le pauvre garçon va lister ici pour lire mon commentaire :)

Il me semble que nous sommes encore trop bornés à façon C-style de pensée. Dans le paradigma POO nous jouer avec des objets, pas les types. Const objet peut être conceptuellement différent d'un objet non-const, en particulier dans le sens de const logique (par opposition au niveau du bit const). Ainsi, même si la fonction correcte de const params est (peut-être) une plus-en cas de prudence PODs il est pas dans le cas d'objets. Si une fonction fonctionne avec un objet const il faut le dire. Considérons l'extrait de code suivant

#include <iostream>

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class SharedBuffer {
private:

  int fakeData;

  int const & Get_(int i) const
  {

    std::cout << "Accessing buffer element" << std::endl;
    return fakeData;

  }

public:

  int & operator[](int i)
  {

    Unique();
    return const_cast<int &>(Get_(i));

  }

  int const & operator[](int i) const
  {

    return Get_(i);

  }

  void Unique()
  {

    std::cout << "Making buffer unique (expensive operation)" << std::endl;

  }

};

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void NonConstF(SharedBuffer x)
{

  x[0] = 1;

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ConstF(const SharedBuffer x)
{

  int q = x[0];

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int main()
{

  SharedBuffer x;

  NonConstF(x);

  std::cout << std::endl;

  ConstF(x);

  return 0;

}

ps .: vous pouvez faire valoir que (const) référence serait plus approprié ici et vous donne le même comportement. Eh bien, à droite. Il suffit de donner une image différente de ce que je pouvais voir ailleurs ...




Être un programmeur VB.NET qui a besoin d'utiliser un programme C ++ avec 50+ fonctions exposées, et un fichier .h qui utilise sporadiquement le qualificatif const, il est difficile de savoir quand accéder à une variable en utilisant ByRef ou ByVal.

Bien sûr, le programme vous indique en générant une erreur d'exception sur la ligne où vous avez fait l'erreur, mais vous devez deviner lequel des 2-10 paramètres est erroné.

Alors maintenant, j'ai la tâche de mauvais goût d'essayer de convaincre un développeur qu'ils devraient vraiment définir leurs variables (dans le fichier .h) d'une manière qui permet une méthode automatisée de créer toutes les définitions de fonction VB.NET facilement. Ils diront alors béatement, « lire la documentation .... »

J'ai écrit un script awk qui parse un fichier .h, et crée toutes les Declare commandes de fonction, mais sans un indicateur quant aux variables R / O vs R / W, il ne fait que la moitié du travail.

MODIFIER:

Encouragé par un autre utilisateur que je suis qui suit;

Voici un exemple d'un (OMI) mal formé entrée .h;

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

Le VB de mon script résultant;

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

Notez le manque « const » sur le premier paramètre. Sans elle, un programme (ou un autre développeur) n'a aucune idée le 1er paramètre doit être passé « ByVal. » En ajoutant le « const » rend le fichier .h auto-documenté afin que les développeurs utilisant d'autres langues peuvent facilement écrire du code de travail.