programmez-en-d/surcharge_fonctions.whata

247 lines
9.6 KiB
Plaintext
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

[set
title = "Surcharge de fonctions"
partAs = "chapitre"
translator = "Olivier Pisano"
proofreader = "Stéphane Goujet"
]
On appelle surcharger une fonction le fait de définir plusieurs fonctions ayant toutes le même nom. Pour pouvoir les différencier, leurs paramètres doivent être différents.
Le code suivant montre plusieurs surcharges de la fonction ``info()``, chacune prenant un type de paramètre différent~ :
[code=d <<<
import std.stdio;
void info(in double nombre)
{
writeln("Virgule flottante~ : ", nombre);
}
void info(in int nombre)
{
writeln("Entier~ : ", nombre);
}
void info(in char[] chaine)
{
writeln("Chaîne de caractères~ : ", chaine);
}
void main()
{
info(1.2);
info(3);
info("bonjour");
}
>>>]
Bien que toutes les fonctions soient nommées ``info()``, le compilateur choisit celle qui correspond à l'argument qui est utilisé lors de l'appel. Ici, la fonction ``info()`` prenant un double est appelée parce que la constante littérale ``1.2`` est de type ``double``.
Le choix de la fonction à appeler est fait à la compilation, ce qui n'est pas toujours facile ou évident. Par exemple, puisque le type ``int`` peut être implicitement converti en type ``double`` et en type ``real``, le compilateur ne peut choisir quelle fonction employer dans le programme suivant~ :
[code=d <<<
real septFois(in real valeur)
{
return 7 * valeur;
}
double septFois(in double valeur)
{
return 7 * valeur;
}
void main()
{
int valeur = 5;
auto resultat = septFois(valeur); // ← ERREUR de compilation
}
>>>]
[p Note~ : | il n'est généralement pas nécessaire d'écrire des fonctions séparées quand les corps des fonctions sont identiques. Nous verrons plus tard dans le chapitre des templates comment écrire une définition de fonction unique qui peut être utilisée avec des types différents.]
Néanmoins, s'il y a une autre surcharge de fonction prenant un paramètre ``long``, alors l'ambigüité est résolue, car ``long`` correspond mieux à ``int`` que ``double`` ou ``real``~ :
[code=d <<<
long septFois(in long valeur)
{
return 7 * valeur;
}
// ...
auto resultat = septFois(valeur); // compile maintenant
>>>>]
[ = Résolution de surcharge
Le compilateur choisit la surcharge qui correspond le mieux aux arguments. C'est ce que l'on appelle la résolution de surcharge.
Bien que la résolution soit simple et intuitive la plupart du temps, ce n'est pas toujours le cas. Voici les règles de résolution de surcharge. Elles sont présentées de manière simplifiée dans ce livre.
Il y a 4 niveaux de correspondance, du pire au meilleur~ :
- pas de correspondance~ ;
- correspondance via conversion automatique de type~ ;
- correspondance via qualification ``const``~ ;
- correspondance exacte.
Le compilateur considère toutes les surcharges d'une fonction pendant la résolution de surcharge. Si la fonction a plusieurs paramètres, le niveau de correspondance global d'une fonction est le plus mauvais des niveaux de correspondance de tous les paramètres.
Après que tous les niveaux de correspondance aient été déterminés, la surcharge avec la meilleure correspondance est choisie. S'il y a plusieurs surcharges qui ont la même correspondance, alors des règles plus complexes sont appliquées. Je ne rentrerai pas dans les détails de ces règles dans ce livre. Si votre programme en est au point où il dépend de règles complexes de résolution de surcharge de fonctions, c'est peut-être un signe qu'il est temps d'en revoir la conception. Une autre option est de profiter d'autres fonctionnalités de D comme des [* templates]. Une approche encore plus simple serait de nommer chaque fonction différemment pour chaque type comme ``septFois_real()`` et ``septFois_double()``.
]
[ = Surcharge de fonctions pour les types définis par l'utilisateur
La surcharge de fonctions s'utilise également avec les structures et les classes. Qui plus est, les ambigüités liées à la surcharge de fonctions sont bien moins fréquentes avec les types définis par l'utilisateur. Surchargeons la fonction ``info()`` précédente avec quelques-uns des types que nous avons définis dans le chapitre sur les structures~ :
[code=d <<<
struct MomentDeLaJournee
{
int heure;
int minute;
}
void info(in MomentDeLaJournee moment)
{
writef("%02s:%02s", moment.heure, moment.minute);
}
>>>]
La surcharge permet aux objets ``MomentDeLaJournee`` d'être utilisés avec ``info()``. Au final, les types définis par l'utilisateur peuvent être affichés de la même manière que les types fondamentaux~ :
[code=d <<<
auto momentPetitDejeuner = MomentDeLaJournee(7, 0);
info(momentPetitDejeuner);
>>>]
Les objets ``MomentDeLaJournee`` correspondent à cette nouvelle surcharge de ``info()``~ :
[output <<<
07:00
>>>]
Le code suivant est une surcharge de ``info()`` pour le type ``Reunion``~ :
[code=d <<<
struct Reunion
{
string sujet;
size_t nombreDeParticipants;
MomentDeLaJournee debut;
MomentDeLaJournee fin;
}
void info(in Reunion reunion)
{
info(reunion.debut);
write('-');
info(reunion.fin);
writef(" réunion \"%s\" avec %s personnes", reunion.sujet,
reunion.nombreDeParticipants);
}
>>>]
Veuillez noter que cette surcharge utilise la surcharge déjà définie pour ``MomentDeLaJournee``. Les objets ``Reunion`` peuvent maintenant être affichés comme le sont les types fondamentaux~ :
[code=d <<<
auto reunionCyclisme = Reunion("Cyclisme", 3, MomentDeLaJournee(9, 0),
MomentDeLaJournee(9, 10));
>>>]
Résultat~ :
[output <<<
09:00-09:10 réunion "Cyclisme" avec 3 personnes
>>>]
]
[ = Limitations
Bien que nos surcharges de la fonction [c info()] soient très pratiques, cette méthode présente quelques limitations~ :
* ``info()`` écrit systématiquement sur la sortie standard. Elle serait nettement plus utile si elle pouvait écrire dans n'importe quel fichier. Une solution pour cela serait de passer également le flux de sortie en paramètre. Par exemple, pour le type ``MomentDeLaJournee``~ :
[code=d <<<
void info(File fichier, in MomentDeLaJournee temps)
{
fichier.writef("%02s:%02s", temps.heure, temps.minute);
}
>>>]
Cela permettrait d'écrire des objets ``MomentDeLaJournee`` dans n'importe quel fichier, y compris la sortie standard~ :
[code=d <<<
info(stdout, momentPetitDejeuner);
auto fichier = File("un_fichier", "w");
info(fichier, momentPetitDejeuner);
>>>]
[p Note~ : | les objets spéciaux ``stdin``, ``stdout`` et ``stderr`` sont de type ``File``.]
* Plus important~ : ``info()`` ne permet pas de produire une représentation textuelle de variables. Par exemple, elle ne permet pas de passer des types utilisateur à ``writeln()``~ :
[code=d <<<
writeln(momentPetitDejeuner); // Écrit avec le format générique, inutile.
>>>]
Le code qui précède affiche l'objet dans un format générique qui contient son type et les valeurs de ses membres, d'une manière qui n'est pas pertinent pour le programme~ :
[code=d <<<
MomentDeLaJournee(7, 0)
>>>]
Il serait beaucoup plus utile d'avoir une fonction qui convertisse des objets MomentDeLaJournee en [c string] dans leur format spécial "``12:34``". Nous verrons comment définir des représentations textuelles d'objets structures dans le prochain chapitre.
]
[ = Exercice
Surchargez la fonction ``info()`` pour les structures suivantes~ :
[code=d <<<
struct Repas
{
MomentDeLaJournee moment;
string adresse;
}
struct planningDeLaJournee
{
Reunion reunionMatin;
Repas repas;
Reunion reunionApresMidi;
}
>>>]
Puisque ``Repas`` a seulement un moment de début, ajoutez une heure et demie pour déterminer sa fin. Vous pouvez utiliser la fonction ``ajouterDuree`` que nous avons définie précédemment dans le chapitre sur les structures~ :
[code=d <<<
MomentDeLaJournee ajouterDuree(in MomentDeLaJournee debut,
in MomentDeLaJournee duree)
{
MomentDeLaJournee resultat;
resultat.minute = debut.minute + duree.minute;
resultat.heure = debut.heure + duree.heure;
resultat.heure += resultat.minute / 60;
resultat.minute %= 60;
resultat.heure %= 24;
return resultat;
}
>>>]
Une fois que les moments de fin des objets repas seront calculés au moyen de ``ajouterDuree()``, les objets ``planningDeLaJournee`` devront être affichés ainsi~ :
[output <<<
10:30-11:45 Réunion "Cyclisme" avec 4 personnes
12:30-14:00 Repas, adresse : Istamboul
15:30-17:30 Réunion "Budget" avec 8 personnes
>>>]
[[part:corrections/surcharge_fonctions | … La solution]]
]