Valeur par défaut du paramètre de fonction

130

1.

int Add (int a, int b = 3);
int Add (int a, int b)
{

}

2.

int Add (int a, int b);
int Add (int a, int b = 3)
{

}

Les deux fonctionnent; quelle est la méthode standard et pourquoi ?

httpinterpret
la source

Réponses:

203

Si vous placez la déclaration dans un fichier d'en-tête, la définition dans un .cppfichier séparé et #includel'en-tête d'un autre .cppfichier, vous pourrez voir la différence.

Plus précisément, supposons:

lib.h

int Add(int a, int b);

lib.cpp

int Add(int a, int b = 3) {
   ...
}

test.cpp

#include "lib.h"

int main() {
    Add(4);
}

La compilation de test.cppne verra pas la déclaration de paramètre par défaut et échouera avec une erreur.

Pour cette raison, la définition de paramètre par défaut est généralement spécifiée dans la déclaration de fonction :

lib.h

int Add(int a, int b = 3);
Greg Hewgill
la source
Ensuite, bsera défini plusieurs fois, une fois pour chaque unité de compilation qui comprend lib.h, n'est-ce pas?
httpinterpret
@httpinterpret: dans un sens oui, la valeur par défaut de best définie une fois pour chaque .cpp fichier contenant l'en-tête. Mais ce n'est pas grave, car vous n'avez qu'une seule déclaration de la Addfonction.
Greg Hewgill
1
@httpinterpret Le compilateur ajoutera le paramètre non spécifié par le paramètre par défaut lorsque le code de l'appelant est généré. C'est pourquoi la valeur par défaut DOIT être dans le prototype de fonction et non dans l'implémentation de la fonction. Le paramètre n'est pas défini au sens de définition de variable puisque le prototype ne définit pas de variables.
harper le
1
Cette réponse pourrait être modifiée car une analyse rapide (en regardant simplement le code et en n'allant pas avant "Pour cette raison") m'a fait comprendre le contraire de ce que vous vouliez dire.
Gabriel Devillers
44

En C ++, les exigences imposées aux arguments par défaut en ce qui concerne leur emplacement dans la liste des paramètres sont les suivantes:

  1. L'argument par défaut pour un paramètre donné ne doit pas être spécifié plus d'une fois. Le spécifier plusieurs fois (même avec la même valeur par défaut) est illégal.

  2. Les paramètres avec des arguments par défaut doivent former un groupe contigu à la fin de la liste de paramètres.

Maintenant, en gardant cela à l'esprit, en C ++, vous êtes autorisé à «agrandir» l'ensemble des paramètres qui ont des arguments par défaut d'une déclaration de la fonction à la suivante, tant que les exigences ci-dessus sont continuellement satisfaites.

Par exemple, vous pouvez déclarer une fonction sans argument par défaut

void foo(int a, int b);

Pour appeler cette fonction après une telle déclaration, vous devrez spécifier les deux arguments explicitement.

Plus tard (plus bas) dans la même unité de traduction, vous pouvez la re-déclarer, mais cette fois avec un argument par défaut

void foo(int a, int b = 5);

et à partir de ce moment, vous pouvez l'appeler avec un seul argument explicite.

Plus bas, vous pouvez le déclarer à nouveau en ajoutant un autre argument par défaut

void foo(int a = 1, int b);

et à partir de ce moment, vous pouvez l'appeler sans arguments explicites.

L'exemple complet pourrait ressembler à ceci

void foo(int a, int b);

int main()
{
  foo(2, 3);

  void foo(int a, int b = 5); // redeclare
  foo(8); // OK, calls `foo(8, 5)`

  void foo(int a = 1, int b); // redeclare again
  foo(); // OK, calls `foo(1, 5)`
}

void foo(int a, int b)
{
  // ...
}

Quant au code de votre question, les deux variantes sont parfaitement valables, mais elles signifient des choses différentes. La première variante déclare immédiatement un argument par défaut pour le deuxième paramètre. La deuxième variante déclare initialement votre fonction sans argument par défaut, puis en ajoute un pour le deuxième paramètre.

L'effet net de vos deux déclarations (c'est-à-dire la façon dont il est vu par le code qui suit la deuxième déclaration) est exactement le même: la fonction a un argument par défaut pour son deuxième paramètre. Cependant, si vous parvenez à presser du code entre la première et la deuxième déclaration, ces deux variantes se comporteront différemment. Dans la deuxième variante, la fonction n'a pas d'arguments par défaut entre les déclarations, vous devrez donc spécifier les deux arguments explicitement.

Fourmi
la source
Je ne pense pas que votre code défini void foo (int a = 1, int b) fonctionnerait. Vous devez avoir tous les paramètres facultatifs après un paramètre facultatif. C'est une erreur de syntaxe (au moins avec g ++ 4.5.3 sur mon système).
Nilesh le
@Nilesh: Comme je l'ai dit explicitement ci-dessus (et c'est tout l'intérêt de cet exemple) pour void foo(int a = 1, int b)fonctionner, il faut le déclarer après void foo(int a, int b = 5) . Oui, cela fonctionnera. Et non, ce n'est pas une erreur de syntaxe. g ++ 4.5.3 le compilera parfaitement.
Du
D'accord, la fonction prend la valeur de b de la déclaration précédente. Obtenir la chose maintenant. Merci :-)
Nilesh
1
@Nilesh: Oui, les déclarations d'arguments par défaut sont accumulées dans toutes les déclarations précédentes dans l'unité de traduction.
Du
1
J'aime écrire mes prototypes de fonctions sans noms de variables, comme int foo(int). Je trouve que je peux à int foo(int=5)nouveau écrire en omettant les noms de paramètres. Personne ne semble encore l'avoir mentionné.
Victor Eijkhout le
5

La première méthode serait préférée à la seconde.

En effet, le fichier d'en-tête montrera que le paramètre est facultatif et quelle sera sa valeur par défaut. En outre, cela garantira que la valeur par défaut sera la même, quelle que soit l'implémentation du fichier .cpp correspondant.

Dans le second cas, il n'y a aucune garantie d'une valeur par défaut pour le second paramètre. La valeur par défaut peut changer, selon la manière dont le fichier .cpp correspondant est implémenté.

utilisateur342264
la source
4

Les arguments par défaut doivent être spécifiés avec la première occurrence du nom de la fonction, généralement dans le prototype de la fonction. Si le prototype de fonction est omis parce que la définition de fonction sert également de prototype, les arguments par défaut doivent être spécifiés dans l'en-tête de la fonction.

clyfe
la source