web-dev-qa-db-fra.com

Où mettre la valeur du paramètre par défaut en C ++?

Quelle est la place pour la valeur du paramètre par défaut? Juste dans la définition de fonction, ou la déclaration, ou les deux endroits?

287
Thomson

Les valeurs de paramètre par défaut doivent figurer sur la déclaration, car c'est la seule chose que voit l'appelant.

EDIT: Comme d'autres le font remarquer, vous pouvez avoir l'argument sur la définition, mais je vous conseillerais d'écrire tout le code comme si ce n'était pas vrai.

292
Marcelo Cantos

Vous pouvez faire l'un ou l'autre, mais jamais les deux. Habituellement, vous le faites à la déclaration de fonction et tous les appelants peuvent utiliser cette valeur par défaut. Cependant vous pouvez le faire à la définition de la fonction et seuls ceux qui verront la définition pourront utiliser la valeur par défaut.

92
sharptooth

C++ place la logique de paramètre par défaut du côté appelant, cela signifie que si l'expression de la valeur par défaut ne peut pas être calculée à partir du lieu de l'appel, la valeur par défaut ne peut pas être utilisée.

Normalement, d’autres unités de compilation incluent uniquement la déclaration, de sorte que les expressions de valeur par défaut placées dans la définition ne peuvent être utilisées que dans l’unité de compilation de définition elle-même (et après la définition, c’est-à-dire après que le compilateur a vu les expressions de valeur par défaut).

L'emplacement le plus utile est dans la déclaration (.h) afin que tous les utilisateurs la voient.

Certaines personnes aiment également ajouter les expressions de valeur par défaut dans l'implémentation (sous forme de commentaire):

void foo(int x = 42,
         int y = 21);

void foo(int x /* = 42 */,
         int y /* = 21 */)
{
   ...
}

Cependant, cela signifie une duplication et ajoutera la possibilité d'avoir un commentaire désynchronisé par rapport au code (quoi de pire qu'un code non commenté? Code avec des commentaires trompeurs!).

78
6502

Bien que ce soit un "ancien" fil de discussion, j'aimerais quand même y ajouter ce qui suit:

J'ai vécu le cas suivant:

  • Dans le fichier d'en-tête d'une classe, j'avais
int SetI2cSlaveAddress( UCHAR addr, bool force );
  • Dans le fichier source de cette classe, j'avais
int CI2cHal::SetI2cSlaveAddress( UCHAR addr, bool force = false )
{
   ...
}

Comme on peut le voir, j’avais mis la valeur par défaut du paramètre "force" dans le fichier source de la classe, pas dans le fichier d’en-tête de la classe.

Ensuite, j'ai utilisé cette fonction dans une classe dérivée comme suit (la classe dérivée a hérité de la classe de base de manière publique):

SetI2cSlaveAddress( addr );

en supposant qu'il prendrait le paramètre "force" comme "faux" "pour acquis".

Cependant, le compilateur ( mis en mode c ++ 11 ) s'est plaint et m'a donné l'erreur de compilation suivante:

/home/.../mystuff/domoproject/lib/i2cdevs/max6956io.cpp: In member function 'void CMax6956Io::Init(unsigned char, unsigned char, unsigned int)':
/home/.../mystuff/domoproject/lib/i2cdevs/max6956io.cpp:26:30: error: no matching function for call to 'CMax6956Io::SetI2cSlaveAddress(unsigned char&)'
/home/.../mystuff/domoproject/lib/i2cdevs/max6956io.cpp:26:30: note: candidate is:
In file included from /home/geertvc/mystuff/domoproject/lib/i2cdevs/../../include/i2cdevs/max6956io.h:35:0,
                 from /home/geertvc/mystuff/domoproject/lib/i2cdevs/max6956io.cpp:1:
/home/.../mystuff/domoproject/lib/i2cdevs/../../include/i2chal/i2chal.h:65:9: note: int CI2cHal::SetI2cSlaveAddress(unsigned char, bool)
/home/.../mystuff/domoproject/lib/i2cdevs/../../include/i2chal/i2chal.h:65:9: note:   candidate expects 2 arguments, 1 provided
make[2]: *** [lib/i2cdevs/CMakeFiles/i2cdevs.dir/max6956io.cpp.o] Error 1
make[1]: *** [lib/i2cdevs/CMakeFiles/i2cdevs.dir/all] Error 2
make: *** [all] Error 2

Mais quand j’ai ajouté le paramètre par défaut dans le fichier de la classe de base:

int SetI2cSlaveAddress( UCHAR addr, bool force = false );

et l'a supprimé du fichier source de la classe de base:

int CI2cHal::SetI2cSlaveAddress( UCHAR addr, bool force )

alors le compilateur était content et tout le code fonctionnait comme prévu (je pourrais donner un ou deux paramètres à la fonction SetI2cSlaveAddress())!

Donc, non seulement pour l'utilisateur d'une classe, il est important de mettre la valeur par défaut d'un paramètre dans le fichier d'en-tête, mais aussi en termes de compilation et de fonctionnement, cela semble être un must!

38
GeertVc

Si les fonctions sont exposées - non membre, publique ou protégée -, l'appelant doit en être informé et les valeurs par défaut doivent se trouver dans l'en-tête.

Si les fonctions sont privées et hors ligne, il est judicieux de mettre les valeurs par défaut dans le fichier d'implémentation, car cela permet des modifications qui ne déclenchent pas la recompilation du client (un problème parfois grave pour les bibliothèques de bas niveau partagées à l'échelle de l'entreprise). développement). Cela dit, cela risque de prêter à confusion et la présentation de l'API de manière plus intuitive dans l'en-tête présente un intérêt particulier pour la documentation. Choisissez donc votre compromis - même si la cohérence est la chose principale lorsqu'il n'y a aucune raison impérieuse.

13
Tony Delroy

la déclaration est généralement la plus "utile", mais cela dépend de la manière dont vous voulez utiliser la classe.

les deux n'est pas valide.

10
justin

Bonne question ... Je trouve que les codeurs utilisent généralement la déclaration pour déclarer les valeurs par défaut. J'ai été retenu dans un sens (ou averti) ou dans l'autre aussi en fonction du compilateur

void testFunct(int nVal1, int nVal2=500);
void testFunct(int nVal1, int nVal2)
{
    using namespace std;
    cout << nVal1 << << nVal2 << endl;
}
10
ice911

Un autre point que je n'ai trouvé personne a mentionné:

Si vous avez une méthode virtuelle, chaque déclaration peut avoir sa propre valeur par défaut!

Cela dépend de l'interface que vous appelez, quelle valeur sera utilisée.

Exemple sur ideone

struct iface
{
    virtual void test(int a = 0) { std::cout << a; }
};

struct impl : public iface
{
    virtual void test(int a = 5) override { std::cout << a; }
};

int main()
{
    impl d;
    d.test();
    iface* a = &d;
    a->test();
}

Il imprime 50

Je vous déconseille vivement de l'utiliser comme ceci

9
relaxxx

Vous pouvez le faire dans l’un ou l’autre (selon la norme), mais rappelez-vous que si votre code affiche la déclaration sans argument (s) par défaut avant la définition contenant l’argument par défaut, une erreur de compilation peut survenir.

Par exemple, si vous incluez un en-tête contenant une déclaration de fonction sans liste d'arguments par défaut, le compilateur recherchera ce prototype car il ne connaît pas les valeurs de vos arguments par défaut et le prototype ne correspond donc pas.

Si vous mettez une fonction avec un argument par défaut dans la définition, incluez ce fichier mais je ne le suggérerai pas.

3
Pervez Alam

Ajouter un point de plus. Les déclarations de fonction avec l'argument par défaut doivent être ordonnées de droite à gauche et de haut en bas.

Par exemple dans la déclaration de fonction ci-dessous si vous modifiez l'ordre de déclaration, le compilateur vous donnera une erreur de paramètre par défaut manquante. La raison pour laquelle le compilateur vous permet de séparer la déclaration de fonction avec l'argument par défaut dans la même portée, mais elle doit être dans l'ordre, de DROITE à GAUCHE (arguments par défaut) et de TOP à BOTTOM (argument par défaut de l'ordre de déclaration de fonction).

//declaration
void function(char const *msg, bool three, bool two, bool one = false);
void function(char const *msg, bool three = true, bool two, bool one); // Error 
void function(char const *msg, bool three, bool two = true, bool one); // OK
//void function(char const *msg, bool three = true, bool two, bool one); // OK

int main() {
    function("Using only one Default Argument", false, true);
    function("Using Two Default Arguments", false);
    function("Using Three Default Arguments");
    return 0;
}

//definition
void function(char const *msg, bool three, bool two, bool one ) {
    std::cout<<msg<<" "<<three<<" "<<two<<" "<<one<<std::endl;
}
3
SridharKritha