Outils d'utilisateurs

Outils du Site


parametres_arguments

Différences

Cette page vous donne les différences entre la révision choisie et la version actuelle de la page.

Lien vers cette vue

parametres_arguments [2016/06/06 17:18]
detork [Surcharge de fonction]
parametres_arguments [2018/06/23 20:25] (Version actuelle)
winjerome Correction orthographique
Ligne 1: Ligne 1:
  
-^ Chapitre précédent ^ [[programmez_avec_le_langage_c|Sommaire principal]] ^ Chapitre suivant ^+ 
 +[[fonctions|Chapitre précédent]] ^ [[programmez_avec_le_langage_c|Sommaire principal]] ^ [[references|Chapitre suivant]] ^
  
  
 ====== Les paramètres de fonctions ====== ====== Les paramètres de fonctions ======
  
-Les variables locales a une fonction ne sont donc pas partageable avec le code qui appelle une fonctionavec d'autres fonctions ou entre plusieurs appels d'une même fonction. Il est donc nécessaire de pouvoir partager des informations entre une fonction et le reste du code. Cela est réalisé via les paramètres de fonction. Pour rappel, la syntaxe générale d'une fonction est la suivante :+Lorsque vous appelez une fonction, les variables locales dans la fonction appelée ou dans la fonction appelante ne sont pas accessibles entre les fonctions (pour rappel, une variable est accessible uniquement dans sa portée). 
 + 
 +<code cpp> 
 +void foo() { 
 +    int i { 123 }; 
 +    std::cout << j << std::endl;  // erreurj n'est pas accessible 
 +
 + 
 +void bar() { 
 +    int j { 123 }; 
 +    foo(); 
 +    std::cout << i << std::endl;  // erreur, i n'est pas accessible 
 +
 +</code> 
 + 
 +Il est donc nécessaire d'avoir un mécanisme pour pouvoir partager des informations entre une fonction et le reste du code. Cela est réalisé via les paramètres de fonction. Pour rappel, la syntaxe générale d'une fonction est la suivante :
  
 <code> <code>
Ligne 13: Ligne 29:
 </code> </code>
  
-Une fonction va donc pouvoir recevoir zéro, un ou plusieurs paramètres en entrées et zéro ou un paramètre en sortie. Pour commencer, vous allez voir les fonctions qui ne prennent que des paramètres en entrée, les paramètres de sortie seront vu ensuite.+Une fonction va donc pouvoir recevoir zéro, un ou plusieurs paramètres en entrée et zéro ou un paramètre en sortie. Pour commencer, vous allez voir les fonctions qui ne prennent que des paramètres en entrée, les paramètres de sortie seront vus ensuite.
  
 ===== Fonction avec un seul paramètre en entrée ===== ===== Fonction avec un seul paramètre en entrée =====
Ligne 25: Ligne 41:
 Par exemple, pour une fonction qui prend un entier et une autre qui prend une chaîne de caractères. Par exemple, pour une fonction qui prend un entier et une autre qui prend une chaîne de caractères.
  
-<code>+<code cpp>
 void f(int i) { void f(int i) {
     ...     ...
Ligne 35: Ligne 51:
 </code> </code>
  
-Pour la fonction ''f'', le paramètre est ''int i'', qui est déclaré avec un type ''int'' et une identifiant ''i''. Même chose pour ''g'', qui contient un paramètre de type ''std::string'' et qui s'appelle ''s''.+Pour la fonction ''f'', le paramètre est ''int i'', qui est déclaré avec un type ''int'' et un identifiant ''i''. Même chose pour ''g'', qui contient un paramètre de type ''std::string'' et qui s'appelle ''s''.
  
 En pratique, un paramètre s'utilise de la même façon qu'une variable locale (en particulier, un paramètre suit les mêmes règles concernant la portée et la durée de vie : vous pouvez utiliser un paramètre dès qu'il est déclaré et jusqu'à la fin de la fonction). En pratique, un paramètre s'utilise de la même façon qu'une variable locale (en particulier, un paramètre suit les mêmes règles concernant la portée et la durée de vie : vous pouvez utiliser un paramètre dès qu'il est déclaré et jusqu'à la fin de la fonction).
Ligne 41: Ligne 57:
 Par exemple, pour afficher les valeurs des paramètres des fonctions ''f'' et ''g''. Par exemple, pour afficher les valeurs des paramètres des fonctions ''f'' et ''g''.
  
-<code>+<code cpp>
 void f(int i) { void f(int i) {
     std::cout << i << std::endl;     std::cout << i << std::endl;
Ligne 51: Ligne 67:
 </code> </code>
  
-Le dernier point à voir est comment appeler une fonction qui prend un paramètre. Pour cela, vous devez tout simplement donner une valeur entre les parenthèses lors de l'appel de la fonction.+Le dernier point à voir est comment appeler une fonction qui prend un paramètre. Pour cela, vous devez donner une valeur entre les parenthèses lors de l'appel de la fonction (cette valeur s'appelle un argument dans un appel de fonction). Une valeur peut être une variable (constante ou non), une littérale ou une expression.
  
-Une valeur pourra être une variable constante ou non et/ou une littérale, selon les types de paramètres. Pour le moment, les paramètres sont "passés par valeur", ce qui autorise l'utilisation de littérales et de variables. Les différents modes de passage de paramètres seront vu dans la suite de ce chapitre.+Il existe différents modes de passage de paramètres, qui seront vus dans la suite. Chaque mode accepte ou non certaines types de valeurs. Pour le moment, le mode de passage de paramètre vu est le "passage par valeur", ce qui autorise l'utilisation de n'importe quel type de valeur. 
 + 
 +Par exemple, pour appeler une fonction qui prend un paramètre :
  
 <code> <code>
Ligne 61: Ligne 79:
 Avec le code d'exemple précédent, vous pouvez donc écrire : Avec le code d'exemple précédent, vous pouvez donc écrire :
  
-<code>+<code cpp>
 #include <iostream> #include <iostream>
  
Ligne 73: Ligne 91:
  
 int main() { int main() {
-    f(123);        // appel avec une littérale entière +    f(123);        // appel avec une littérale de type entier 
-    g("hello");    // appel avec une littérale chaîne+    g("hello");    // appel avec une littérale de type chaîne
          
     const int i { 456 };     const int i { 456 };
-    f(i);          // appel avec une variable+    f(i);          // appel avec une variable de type entier
     const std::string w { "world "};     const std::string w { "world "};
-    g(w);          // appel avec une variable+    g(w);          // appel avec une variable de type chaîne
 } }
 </code> </code>
Ligne 92: Ligne 110:
 </code> </code>
  
-Notez bien que même si dans cet exemple les variables dans la fonction ''main'' et la fonctions ''f'' ont le même nom ''i'', ce sont bien deux variables différentes (elles ne sont pas dans le même contexte). Comme vous le voyez avec la fonction ''g'', il n'y a aucune obligation d'utiliser le même nom entre l'appel de la fonction et le paramètre. (Heureusement ! Comme vous pouvez appeler une fonction plusieurs fois avec des valeurs différentes, cela serait très limitant de devoir utiliser le même nom).+Notez bien que même si dans cet exemple les variables dans la fonction ''main'' et la fonction ''f'' ont le même nom ''i'', ce sont bien deux variables différentes (elles ne sont pas dans le même contexte). Comme vous le voyez avec la fonction ''g'', il n'y a aucune obligation d'utiliser le même nom entre l'appel de la fonction et le paramètre.
  
-<note>Vocabulaire+Ce qui est logique et souhaitable. Comme vous pouvez appeler une fonction plusieurs fois avec des valeurs différentes (et donc des variables différentes), cela n'aurait pas de sens que l'identifiant dans le paramètre de fonction soit identique à l'identifiant utilisé pour appeler la fonction.
  
-Il existe deux termes qui sont proches, mais qui ont un sens légèrement différent : &quot;paramètre&quotet ";argument". Dans la déclaration de la fonction, vous trouver des "paramètres de fonction". Dans l'appel de fonction, ce sont des "arguments de fonction";.+&lt;code cpp&gt; 
 +const int i { 123 }; 
 +f(i) // ok
  
-<code>+const int j {456 }; 
 +f(j);  // ok 
 +</code> 
 + 
 +<note>**Paramètre et argument** 
 + 
 +Il existe deux termes qui sont proches, mais qui ont un sens légèrement différent : "paramètre" et "argument". Dans la déclaration de la fonction, vous trouvez des "paramètres de fonction". Dans l'appel de fonction, ce sont des "arguments de fonction". 
 + 
 +<code cpp>
 void f(paramètres...) { void f(paramètres...) {
 } }
Ligne 108: Ligne 136:
 </code> </code>
  
-Beaucoup de personne confondent les deux notions et cela n'est généralement pas problématique. Mais c'est souvent à ce genre de petits détails que l'on reconnait un développeur qui connait son sujet.+Beaucoup de personnes confondent les deux notions et cela n'est généralement pas problématique. Mais c'est souvent à ce genre de petits détails que l'on reconnait un développeur qui connait son sujet.
 </note> </note>
  
Ligne 122: Ligne 150:
 Il n'y a pas de limite théorique dans la norme C++ sur le nombre de paramètres que vous pouvez écrire (__A vérifier__). Par contre, les compilateurs imposent en général une limite. Et de toute façon, une fonction qui prend trop de paramètres deviendra difficilement lisible et c'est généralement le signe qu'il y a un problème d'organisation dans le code. Il n'y a pas de limite théorique dans la norme C++ sur le nombre de paramètres que vous pouvez écrire (__A vérifier__). Par contre, les compilateurs imposent en général une limite. Et de toute façon, une fonction qui prend trop de paramètres deviendra difficilement lisible et c'est généralement le signe qu'il y a un problème d'organisation dans le code.
  
-Par exemple, pour écrire une fonction qui prendre en paramètre un entier et une chaîne :+Par exemple, pour écrire une fonction qui prend en paramètre un entier et une chaîne :
  
 <code cpp> <code cpp>
Ligne 131: Ligne 159:
 </code> </code>
  
-Une fonction qui prend plusieurs paramètres s'appelle en donnant la liste des arguments, séparés aussi par des virgules.+Une fonction qui prend plusieurs paramètres s'appelle en donnant la liste des arguments, séparés aussi par des virgules. Chaque argument est indépendant, vous pouvez mélanger des variables, des constantes, des littérales, des expressions, selon ce qui est autorisé pour chaque paramètre (selon le mode).
  
 <code cpp> <code cpp>
Ligne 139: Ligne 167:
 <note>**std::pair, std::tuple, structures de données** <note>**std::pair, std::tuple, structures de données**
  
-Même si un paramètre correspond à un type, rien n'interdit que ce soit un type complexe, comme par exemple une ''std::pair'', un ''std::tuple'', ou une structure de données. Chacun de ces types peut contenir plusieurs autres types, ce qui permet en pratique de passer en paramètres de fonction autant de types que vous souhaitez.+Même si un paramètre correspond à un type, rien n'interdit que ce soit un type complexe, comme par exemple un ''std::pair'', un ''std::tuple'', ou une structure de données. Chacun de ces types peut contenir plusieurs autres types, ce qui permet en pratique de passer en paramètres de fonction autant de types que vous souhaitez.
  
-Il faut donc éviter de passer trop d'information à une fonction et faire en sorte que les paramètres aient une cohérence entre eux. Par exemple, ''std::string'' est une structure de données complexe, qui contient d'autres information en interne. Mais cela ne pose pas de problème, puisque ce type est manipulé comme un tout cohérent, sans avoir besoin de connaître ce qu'il contient exactement.+Il faut donc éviter de passer trop d'information à une fonction et faire en sorte que les paramètres aient une cohérence entre eux. Par exemple, ''std::string'' est une structure de données complexe, qui contient d'autres informations en interne. Mais cela ne pose pas de problème, puisque ce type est manipulé comme un tout cohérent, sans avoir besoin de connaître ce qu'il contient exactement. Seul ce que représente ce type (une chaîne de caractères) est important pour comprendre le sens de la fonction.
  
-Avec ''std::pair'' et ''std::tuple'', la situation est différente. En utilisant ces types, vous pouvez passer n'importe quelles informations, même des informations qui n'ont pas de lien logique entre elles. Même si cela semble être une plus grande liberté, cela nuit en fait à la compréhension du code. Il est donc assez rare de trouver des fonctions utilisant ''std::pair'' et ''std::tuple'' en paramètres. la place, il est préférable de créer une structure de données, qui aura un nom explicite et facilitera la compréhension du code.+Avec ''std::pair'' et ''std::tuple'', la situation est un peu différente des structures de données. En utilisant ces types, vous pouvez passer n'importe quelles informations, même des informations qui n'ont pas de lien logique entre elles. Même si cela semble être une plus grande liberté, cela nuit en fait à la compréhension du code. Il est donc assez rare de trouver des fonctions utilisant ''std::pair'' et ''std::tuple'' en paramètres. À la place, il est préférable de créer une structure de données, qui aura un nom explicite et facilitera la compréhension du code.
 </note> </note>
  
Ligne 149: Ligne 177:
 ===== Correspondance entre les paramètres et arguments ===== ===== Correspondance entre les paramètres et arguments =====
  
-La liste des arguments doit correspondre à la liste des paramètres :+La liste des arguments doit correspondre à la liste des paramètres, aussi bien le nombre que les types.
  
 <code cpp main.cpp> <code cpp main.cpp>
Ligne 171: Ligne 199:
 </code> </code>
  
-Exemple de message d'erreur pour ''f'' (clang)+==== Messages d'erreur des compilateurs ==== 
 + 
 +Lorsqu'un compilateur trouve une fonction qui pourrait correspondre à un appel de fonction (avec le même nom de fonction), mais dont le nombre d'arguments ou les types ne correspondent pas, il produit généralement un message d'erreur indiquant qu'il ne trouve pas de fonction correspondante ("no matching function"et donne la liste des fonctions qui pourraient convenir. 
 + 
 +Il est donc très important de bien lire les messages du compilateur, cela facilite grandement la résolution des problèmes, en indiquant où chercher. 
 + 
 + 
 +Par exemple, un message d'erreur classique produit par le compilateur Clang dans cette situation :
  
 <code> <code>
Ligne 183: Ligne 218:
 </code> </code>
  
-Le compilateur indique qu'il ne trouve pas de fonction qui s'appelle ''f'' et dont les paramètres sont compatible avec l'appel ''f(123)'' ("//no matching function//"). Il indique à la ligne suivante qu'il connait une fonction ''f'' qui pourrait être candidate ("//candidate function//"), mais qui prend zéro argument ("//requires 0 arguments//"), alors que l'appel utilise un argument ("//but 1 was provided//").+Le compilateur indique qu'il ne trouve pas de fonction qui s'appelle ''f'' et dont les paramètres sont compatibles avec l'appel ''f(123)'' ("no matching function"). Il indique à la ligne suivante qu'il connait une fonction ''f'' qui pourrait être candidate ("candidate function"), mais qui prend zéro argument ("requires 0 arguments"), alors que l'appel utilise un argument ("but 1 was provided").
  
 Pour l'appel de la fonction ''g'', le message est le suivant : Pour l'appel de la fonction ''g'', le message est le suivant :
Ligne 199: Ligne 234:
 De la même manière, le compilateur indique qu'il ne trouve pas de fonction correspondant à l'appel ''g()'', mais qu'il trouve une fonction qui se nomme ''g'' et qui prend un argument. De la même manière, le compilateur indique qu'il ne trouve pas de fonction correspondant à l'appel ''g()'', mais qu'il trouve une fonction qui se nomme ''g'' et qui prend un argument.
  
-En plus d'avoir le nombre d'arguments dans un appel de fonction qui correspond au nombre de paramètres déclarés dans une fonction, il faut aussi que les types soient les mêmes ou être implicitement convertible par le compilateur. Par exemple, le compilateur saura convertir sans problème un argument de type entier ''int'' en paramètre de type réel ''double'' ou une littérale chaîne (de type ''const char*'') en paramètre de type ''std::string''.+ 
 +==== Correspondance entre les types ==== 
 + 
 +En plus d'avoir le nombre d'arguments dans un appel de fonction qui doit correspondre au nombre de paramètres déclarés dans une fonction, il faut aussi que les types soient exactement les mêmes ou être implicitement convertibles. 
 + 
 +Un type est "implicitement convertible" (sous entendu "par le compilateur") lorsque le compilateur connaît une conversion entre les types et qu'il a le droit de réaliser automatiquement cette conversion. 
 + 
 +Par exemple, le compilateur saura convertir sans problème un argument de type entier ''int'' en paramètre de type réel ''double'' ou une littérale chaîne (de type ''const char*'') en paramètre de type ''std::string''. Par contre, il n'est pas possible de convertir un ''std::string'' en ''std::vector<double>'' (conversion impossible) ou en ''const char*'' (cette conversion est possible en appelant la fonction membre ''std::string::c_str'', mais le compilateur ne peut pas l'appeler implicitement : il faut que la conversion soit écrite //explicitement// par le développeur).
  
 <code cpp> <code cpp>
Ligne 209: Ligne 251:
  
 int main() { int main() {
-    f(123);     // conversion de int entre double+    f(123);     // conversion de int en double
     g("hello"); // conversion de const char* en string     g("hello"); // conversion de const char* en string
 } }
Ligne 243: Ligne 285:
 2 warnings generated. 2 warnings generated.
 12 12
-56+12
 </code> </code>
  
 Notez que le compilateur fait la différence entre la conversion d'une littérale (//-Wliteral-conversion//) et la conversion d'une valeur réelle (//-Wfloat-conversion//). Le résultat de ces arrondis est visible dans le résultat affiché, puisque seules les parties entières sont conservées. Notez que le compilateur fait la différence entre la conversion d'une littérale (//-Wliteral-conversion//) et la conversion d'une valeur réelle (//-Wfloat-conversion//). Le résultat de ces arrondis est visible dans le résultat affiché, puisque seules les parties entières sont conservées.
  
-Pour terminer, lorsque les types des arguments et des paramètres ne sont pas du tout compatible, le compilateur indique une erreur spécifique (//no known conversion//).+Pour terminer, lorsque les types des arguments et des paramètres ne sont pas du tout compatibles, le compilateur indique une erreur spécifique (//no known conversion//).
  
 <code cpp main.cpp> <code cpp main.cpp>
Ligne 274: Ligne 316:
 1 error generated. 1 error generated.
 </code> </code>
- 
- 
  
  
 ===== Retour de fonction ===== ===== Retour de fonction =====
  
-Idem dans l'autre sens :+Le paramètre de retour de fonction est une valeur qui est transmise uniquement depuis la fonction vers le code appelant. Pour rappel, la signature générale d'une fonction qui retourne une valeur est la suivante :
  
 <code> <code>
-void f() +PARAMETRE_RETOUR NOM(...)
-    int i {}; +
-+
- +
-int main() { +
-    f(); // i existe dans f() +
-    std::cout << i << std::endl; // erreur, i n'existe pas dans ce bloc +
-}+
 </code> </code>
  
-Une variable déclarée localement dans une fonction ne sera pas accessible dans le code qui appelle cette fonction. Utilisation de retour de fonctionpermet de retourner 1 seule valeurUtilisation du mot-clé return pour indiquer la valeur que la fonction doit retourner et remplacer void par le type de la valeur que l'on veut retourner.+Lorsqu'une fonction ne retourne aucune valeur, le ''PARAMETRE_RETOUR'' est remplacé par ''void'' (qui est un type très particulierpuisqu'il représente "pas de type")Si la fonction retourne une valeur, ''PARAMETRE_RETOUR'' est remplacé par le type de la valeur que la fonction retourne.
  
-<code> +<code cpp
-int f() +int                 f();  // retourne un entier 
-    int const i { 123 }+std::string         g();  // retourne une chaîne 
-    return i; +std::vector<double&gth() // retourne un tableau
-+
- +
-int main() { +
-    int const j = f(); +
-    std::cout <&ltj <;< std::endl; +
-}+
 </code> </code>
  
-Lorsque l'on appelle f, la variable i dans f est créée et initialisée avec la littérale 123. après le returnla valeur de i est retournée au code appelant, la variable j est créée et initialisée en copiant la valeur retournée par la fonction f (elle copie i) tandis que la variable i est détruite.+Dans le corps de la fonction, la valeur retournée est indiquée par le mot-clé ''return'' suivi de la valeur à retourner. Comme pour les paramètres d'entrées de fonctions, la valeur peut être une littérale, une variable, une constante ou une expression. Et le type de la valeur doit être identique ou implicitement convertible dans le type de retour de la fonction.
  
-retourner directement une valeur : +<code cpp>
- +
-<code>+
 int f() { int f() {
-    return 123;+    return 1 // retourne la valeur entière 1 (littérale)
 } }
  
-int main() { +int g() { 
-    int const j = f()+    const int i { 123 }
-    std::cout << j << std::endl;+    return i // retourne la valeur entière 123 (constante)
 } }
 </code> </code>
  
-Portée de variable fait que l'on peut utiliser 2 variables de même noms si portée différentes. Par exemple :+Bien sûr, rien n'interdit d'utiliser des paramètres d'entrée pour calculer la valeur à retourner. Par exemple :
  
-<code> +<code cpp
-int f() { +int f(int i
-    int const i { 123 }; +    return ++i;  // retourne la valeur entière i+1 (expression)
-    return i; +
-+
- +
-int main() { +
-    int const = f()+
-    std::cout << i << std::endl;+
 } }
 </code> </code>
  
-Il faut bien comprendre ici que même si les 2 variables dans la fonction f et dans main s'appellent toutes les 2 "i", ce sont 2 variables différentes.+Cette dernière syntaxe montre bien l'origine des termes "paramètres d'entréeet "paramètre de sortie" : une fonction prend des valeursréalise des calculs dessus, puis retourne une valeur comme résultat. C'est très proche du concept de fonction en mathématique.
  
-Mot clé return retourne immédiatement de la fonction. Si on écrit :+<note>**Fonction pure**
  
-<code+Une fonction similaire au dernier code, qui prend uniquement des paramètres en entrée sans les modifier, puis retourne une valeur, est appelée une fonction pure en programmation fonctionnelle. Cela aura un intérêt particulier, notamment pour comprendre le comportement d'une fonction et écrire des tests sur cette fonction. Cela sera vu dans le chapitre sur les tests unitaires.
-int f() { +
-    int const i { 123 }; +
-    return i; +
-    std::cout << "on est après le return" << std::endl; // n'est jamais exécuté +
-}+
  
-int main() { +Mais dans de nombreux cas, une fonction ne sera pas pure. Une fonction deviendra impure si elle :
-    int const i = f(); +
-    std::cout << i << std::endl; +
-+
-</code>+
  
-le cout après le return n'est pas exécuté.+  * modifie les paramètres en entrée (ce qui est possible en utilisant des indirections, qui seront vues dans le chapitre suivant) ; 
 +  * en accédant à des variables externes à la fonction (des variables globales).
  
-===== Surcharge de fonction =====+La simple utilisation de ''std::cout'' est suffisante pour rendre une fonction impure. 
 +</note>
  
-(ou //overloading// ou polymorphisme ad-hoc)+La valeur retournée par une fonction est directement accessible dans le code appelant la fonction. L'appel de la fonction peut être directement utilisé comme valeur, ce qui permet de l'affecter à une variable ou de l'utiliser dans une expression. (En fait, un appel de fonction EST une expression).
  
-Polymorphisme : plusieurs fonctions de même nomDes fonctions peuvent avoir le même nom, tant que les paramètres sont différents :+<code cpp main.cpp> 
 +#include <iostream>
  
-<code> +int f() { return 123; } 
-void f(int i) { +int g(int i) { return ++i; }
-    std::cout << "f(int) avec i=" << i << std::endl; +
-+
- +
-void f(string s) { +
-    std::cout <;< "f(string) avec s=" << s << std::endl; +
-+
-</code> +
- +
-Le compilateur choisit la fonction correspondante, selon le type que l'on donne en argument : +
- +
-<code cpp> +
-void f(int i) { +
-    std::cout << "f(int) avec i=" << i << std::endl; +
-+
- +
-void f(long int i) { +
-    std::cout << "f(long int) avec i="<< i << std::endl; +
-}+
  
 int main() { int main() {
-    f(1); // 1 est une littérale de type int +    // initialisation dans une variable 
-    f(2L); // 2L est une littérale de type long int +    const auto i = f(); 
- +    
-    int i { 1 }; +    // utilisation dans une expression 
-    f(i); +    std::cout <;< (f() + g(123)) <;<std::endl;
- +
-    long int l { 2 }; +
-    f(l);+
 } }
 </code> </code>
  
-affiche :+Une fonction qui retourne une valeur doit obligatoirement avoir au moins un ''return''. Si ce n'est pas le cas, le compilateur produit un message d'erreur :
  
 <code> <code>
-f(int) avec i=1 +main.cpp:3:15: warning: control reaches end of non-void function [-Wreturn-type] 
-f(long int) avec i=2 + } 
-f(int) avec i=1 + ^
-f(long int) avec i=2+
 </code> </code>
  
-Le compilateur commence par rechercher s'il connait une fonction avec le nom correspondant. Par exemple pour f(1), il trouve 2 fonctions : f(int) et f(long int). Ensuite il regarde si l'un des types en paramètre correspondant au type en argument. Ici, c'est le cas, il appelle donc f(int).+(Ce qui signifie "atteint la fin de la fonction qui n'est pas void").
  
-Si on écrit :+Il est possible d'avoir plusieurs ''return'' dans une fonction. Lorsque la fonction arrive à un ''return'', elle se termine immédiatement et la suite du code appelant est exécuté.
  
 <code cpp> <code cpp>
-#include <iostream> +int f() { 
- +    return 123; 
-void f(long int i) { +    const int i { 456 }
-    std::cout <;< "f(long int) avec i="<< i << std::endl; +    return i;
-+
- +
-int main() { +
-    f(1)// 1 est une littérale de type int+
 } }
 </code> </code>
  
-Le compilateur trouve la fonction f, mais le paramètre ne correspond pasIl regarde s'il peut faire une conversion. Ici, oui, on peut convertir implicitement un int en long int. il convertie donc 1 en 1L et appelle f(long int).+Le code à la suite du premier ''return'' n'est jamais exécuté et la fonction retourne toujours 123(Cela n'est pas très utile dans un code aussi simple d'avoir plusieurs ''return'', cela sera intéressant quand vous concevrez des algorithmes plus complexes).
  
-S'il ne trouve pas de conversion possibleil lance un message d'erreurPar exemple, si on appelle f("du texte"), le compilateur donne :+Le mot-clé ''return'' peut également être utilisé avec une fonction qui ne retourne aucune valeur. Dans ce cas''return'' n'est suivi d'aucune valeur.
  
-<code> 
-main.cpp:19:5: error: no matching function for call to 'f' 
-    f("une chaine"); 
-    ^ 
-main.cpp:3:6: note: candidate function not viable: no known conversion  
-from 'const char [11]' to 'int' for 1st argument 
-void f(int i) { 
-     ^ 
-main.cpp:7:6: note: candidate function not viable: no known conversion  
-from 'const char [11]' to 'long' for 1st argument 
-void f(long int i) { 
-     ^ 
-1 error generated. 
-</code> 
- 
-Ce qui signifie qu'il ne trouve aucune fonction correspond à l'appel de f("une chaine"), mais qu'il a 2 candidat (2 fonction qui ont le même nom) mais sans conversion possible ("no known conversion"). 
- 
-Au contraire, dans certain cas, il aura plusieurs possibilités, soit parce que vous déclarez par erreur 2 fonctions avec les mêmes paramètres, soit parce que le compilateur peut faire 2 conversions pour 2 types. Dans le premier cas : 
- 
-<code> 
-void f() { 
-   std::cout << "première fonction f" << std::endl; 
-} 
- 
-void f() { 
-   std::cout << "seconde fonction f" << std::endl; 
-} 
-</code> 
- 
-produit le message  : 
- 
-<code> 
-main.cpp:7:6: error: redefinition of 'f' 
-void f(int i) { 
-     ^ 
-main.cpp:3:6: note: previous definition is here 
-void f(int i) { 
-     ^ 
-</code> 
-      
-Quand le compilateur arrive à la ligne 7 et rencontre la seconde fonction f (qu'il connait déjà), il prévient qu'il connait déjà ("redefinition of 'f'") et que la première version ("previous definition is here") se trouve à la ligne 3. 
- 
-L'autre cas est si plusieurs fonctions peuvent correspondent, l'appel est ambigu. Par exemple : 
  
 <code cpp> <code cpp>
-#include <iostream> 
- 
 void f(int i) { void f(int i) {
-    std::cout << "f(int) avec i=" << i << std::endl; +    return
-+    ++i;
- +
-void f(long int i) { +
-    std::cout << "f(long int) avec i=" << i << std::endl; +
-+
- +
-int main() { +
-    f(1u)// 1 est une littérale de type unsigned int+
 } }
 </code> </code>
  
-affiche le message d'erreur :+<note>**La fonction main**
  
-<code> +Un cas particulier avec la fonction ''main'' : vous avez peut être réalisé que la signature de cette fonction indique qu'elle doit retourner une valeur entière ''int''. Mais dans les codes d'exemple de coursle mot-clé ''return'' n'a jamais été utilisé jusqu'à présent.
-main.cpp:12:5: error: call to 'fis ambiguous +
-    f(1u); // 1 est une littérale de type int +
-    ^ +
-main.cpp:3:6: note: candidate function +
-void f(int i) { +
-     ^ +
-main.cpp:7:6: note: candidate function +
-void f(long int i) { +
-     ^ +
-</code> +
- +
-Il existe une conversion de unsigned int vers int et vers long int. Il n'y a pas de priorité dans les conversions, le compilateur ne sait pas quelle conversion choisir et donc quelle fonction appeler. L'appel est ambuigu ("call to 'f' is ambiguous"), il trouve deux fonctions candidate ("candidate function"). +
- +
-La méthode qui permet au compilateur de trouver la fonction correspondant à une appel s'appelle la résolution des noms (name lookup) +
- +
-<note warning>Note sur bool +
- +
-Comme cela a déjà été expliqué, certains types, dont les littérales chaînes (et plus généralement les pointeurs), sont convertissable automatiquement en booléen. Si on écrit la surcharge suivante : +
- +
-<code cpp> +
-void foo(bool) { std::cout << "f(bool)" << std::endl; } +
-void foo(string const&) { std::cout << "f(string)" << std::endl; } +
- +
-foo("abc"); +
-</code> +
- +
-Ce code ne va pas afficher ''f(string)'', mais ''f(bool)''. Si on ajoute une fonction ''f(const char*)'', elle sera appelée en premier. La raison est que la littérale chaîne est de type ''const char*'', les fonctions seront appelée dans l'ordre suivant : +
- +
-  * f(const char*) : par de conversion entre l'argument et le paramètre ; +
-  * f(bool) : conversion automatique ; +
-  * f(string) : conversion passant par une classe. +
- +
-Donc attention lorsque vous écrivez une fonction qui prend bool, elle peut prendre aussi n'importe quel pointeur. +
- +
-Solution C++14 : écrire "abc"s pour créer une littérale de type string.+
  
-__Détailler le name lookup__+La raison est que la fonction ''main'' est la seule exception à cette obligation de toujours avoir au moins un ''return'' dans une fonction qui retourne une valeur. Dans le cas de la fonction ''main'', si ''return'' n'est pas présent, la fonction ''main'' retourne par défaut la valeur 0 (qui indique que le programme s'est déroulé correctement).
 </note> </note>
  
Ligne 528: Ligne 428:
 ===== Valeurs par défaut ===== ===== Valeurs par défaut =====
  
 +Vous avez vu que pour appeler une fonction, il est nécessaire de fournir autant d'arguments lors de l'appel qu'il y a de paramètres dans la déclaration de la fonction. En fait, ce n'est pas tout à fait vrai. Il est possible de fournir des paramètres par défaut lors de la déclaration d'une fonction. Lors de l'appel, ces paramètres seront optionnels et il sera possible de ne pas fournir d'arguments pour ceux-ci.
  
 +Pour donner un paramètre par défaut à une fonction, la syntaxe est assez proche d'une initialisation de variable. La différence est que seule la syntaxe avec le signe ''='' est acceptée.
  
 +<code>
 +TYPE PARAMETRE = VALEUR_PAR_DEFAUT
 +</code>
  
 +Par exemple, pour écrire une fonction qui peut prendre un paramètre entier optionnel.
  
-On peut souhaiter pouvoir appeler une fonction avec et sans un argument+<code cpp main.cpp> 
 +#include <iostream>
  
-Par exemple f qui prend un entier ou 0 si on en donne aucune valeur +void f(int i = 0) { 
- +    std::cout << i << std::endl;
-Première solution, surcharger la fonction : +
- +
-<code> +
-void f() { +
-    std::cout << "f()" << std::endl; +
-+
- +
-void f(int i) { +
-    std::cout << "f(int) avec i=" << i << std::endl;+
 } }
  
 int main() { int main() {
-    f();    // ok, appel de f() +    f(123);  // appel avec un argument 
-    f(123); // ok, appel de f(int i)+    f();     // appel avec le paramètre par défaut
 } }
 </code> </code>
  
-Le compilateur trouve à chaque fois deux fonctions avec le même nom, mais pas d’ambiguïté pour savoir laquelle appeler. +affiche :
- +
-Possibilité de simplifier en donnant une valeur par défaut à un paramètre :+
  
 <code> <code>
-void f(int i = 0) { +123 
-    std::cout << "f(int) avec i=" << i << std::endl; +0
-}+
 </code> </code>
  
-Dans ce cason indique que f peut prendre un entier. Si on ne donne pas de valeur, le compilateur peut utiliser la valeur par défaut :+Lorsqu'une fonction a plusieurs paramètresune nouvelle règle s'ajoute : il n'est pas possible de faire suivre un paramètre avec une valeur par défaut par un paramètre sans valeur par défaut.
  
-<code> +<code cpp
-int main() { +void f(int i = 0, int j = 0, int k = 0);  // ok 
-    f();    // ok, appel de f(int i) avec i = 0 +void g(int i    , int j = 0, int k = 0);  // ok 
-    f(123); // ok, appel de f(int i) avec i = 123 +void h(int i = 0, int j = 0, int k    );  // erreur
-}+
 </code> </code>
  
-Bien sûril ne faut pas laisser les 2 fonctions, pour éviter les ambiguité :+La fonction ''h'' possède un paramètre ''k'' sans valeur par défautalors que les paramètres ''i'' et ''j'' ont des valeurs par défaut.
  
-<code> +La fonction ''f'' a trois paramètres optionnels, alors que la fonction ''g'' a un paramètre obligatoire (''i''et deux paramètres optionnels (''j'' et ''k'').
-void f() +
-    std::cout << "f()" << std::endl; +
-}+
  
-void f(int i = 0) { +<code cpp&gt; 
-    std::cout <&lt; ";f(intavec i=" <<; i << std::endl+f(1, 2, 3);   // i=1, j=2, k=3 
-}+f(1, 2)     // i=1, j=2, k=0 
 +f(1)        // i=1, j=0, k=0 
 +f();          // i=0, j=0, k=0
  
-int main() { +g(1, 2, 3);   // i=1, j=2, k=3 
-    f();    // erreurappel de f() ou de f(int i) avec i = 0 ? +g(1, 2);      // i=1j=2, k=0 
-}+g(1);         // i=1, j=0, k=0 
 +g();          // erreur, i n'est pas optionnel
 </code> </code>
  
  
-^ Chapitre précédent ^ [[programmez_avec_le_langage_c|Sommaire principal]] ^ Chapitre suivant ^+[[fonctions|Chapitre précédent]] ^ [[programmez_avec_le_langage_c|Sommaire principal]] ^ [[references|Chapitre suivant]] ^
  
parametres_arguments.1465226324.txt.gz · Dernière modification: 2016/06/06 17:18 par detork