IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Cours complet pour apprendre à programmer en D


précédentsommairesuivant

53. Surcharge de fonctions

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 :

 
Sélectionnez
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 :

 
Sélectionnez
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
}

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'ambiguïté est résolue, car long correspond mieux à int que double ou real :

 
Sélectionnez
long septFois(in long valeur)
{
    return 7 * valeur;
}
 
// ...
 
auto resultat =  septFois(valeur);  // compile maintenant

53-1. 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 quatre 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 ont é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().

53-2. 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 ambiguïté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 :

 
Sélectionnez
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(). Finalement, les types définis par l'utilisateur peuvent être affichés de la même manière que les types fondamentaux :

 
Sélectionnez
auto momentPetitDejeuner =  MomentDeLaJournee(7, 0);
info(momentPetitDejeuner);

Les objets MomentDeLaJournee correspondent à cette nouvelle surcharge de info() :

 
Sélectionnez
00:00:00

Le code suivant est une surcharge de info() pour le type Reunion :

 
Sélectionnez
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 :

 
Sélectionnez
auto reunionCyclisme = Reunion("Cyclisme", 3, MomentDeLaJournee(9, 0),
                            MomentDeLaJournee(9, 10));

Résultat :

 
Sélectionnez
09:00-09:10 réunion "Cyclisme" avec 3 personnes

53-3. Limitations

Bien que nos surcharges de la fonction 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 :

     
    Sélectionnez
    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 :

     
    Sélectionnez
    info(stdout, momentPetitDejeuner);
     
    auto fichier = File("un_fichier", "w");
    info(fichier, momentPetitDejeuner);
  • 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() :

     
    Sélectionnez
    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 pertinente pour le programme :

     
    Sélectionnez
    MomentDeLaJournee(7, 0)
  • Il serait beaucoup plus utile d'avoir une fonction qui convertisse des objets MomentDeLaJournee en string dans leur format spécial "12:34". Nous verrons comment définir des représentations textuelles d'objets structures dans le prochain chapitre.

53-4. Exercice

Surchargez la fonction info() pour les structures suivantes :

 
Sélectionnez
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 :

 
Sélectionnez
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 :

 
Sélectionnez
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

La solutionSurcharge de fonctions - Correction.


précédentsommairesuivant