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

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage C++ Discussion :

template bon en paramètre de fonction mais pas en sortie


Sujet :

Langage C++

  1. #1
    Membre régulier
    Profil pro
    Inscrit en
    Novembre 2005
    Messages
    128
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2005
    Messages : 128
    Points : 122
    Points
    122
    Par défaut template bon en paramètre de fonction mais pas en sortie
    Bonjour à tous.

    Pourquoi le programme suivant (exercice 8-3 du livre Accelerated C++ ) ne marche pas?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    Erreur à la ligne: « double result = median(begin, end); »
    erreur: no matching function for call to ‘median(__gnu_cxx::__normal_iterator<double*, std::vector<double, std::allocator<double> > >&, __gnu_cxx::__normal_iterator<double*, std::vector<double, std::allocator<double> > >&)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    #include <iostream>
    #include <vector>
    #include <algorithm>
    #include <stdexcept>
     
    using namespace std;
     
    template <class It, class T>
    T median(It begin, It end) {
    	if (begin == end) 
    		throw domain_error("median of an empty container");
    	sort(begin, end);
    	--end;
    	while (*begin < *end) {
    		++begin;
    		--end;
    	}
    	return begin != end ? (*begin + *end) / 2 : *begin;
    }
     
    int main() { 
    	vector<double> numbers; 
     
    	numbers.push_back(1); 
    	numbers.push_back(3);
    	numbers.push_back(4);
    	numbers.push_back(5); 
    	numbers.push_back(2);
    	numbers.push_back(6);
     
    	vector<double>::iterator begin = numbers.begin(); 
    	vector<double>::iterator end = numbers.end(); 
     
    	double result = median(begin, end); 
    	cout << result << endl;
    	return 0;
     }
    Ça fonctionne en supprimant « , class T » et en remplaçant T par double, mais je perds la templétisation du renvoie de la fonction.

    Je pensais que la compilation de median avec un type de sortie double se ferait lorsque la ligne « double result = ... » serait vu.

    Même en écrivant la fonction median à part dans un *.h (comme recommandé ici), le problème persiste.

    Message édité pour remplacer « exercice 8-1 » par « exercice 8-3 »

  2. #2
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Salut,
    La valeur retour d'une fonction n'entre jamais en compte dans une résolution (que ce soit du générique ou de la surcharge). Tu dois alors préciser le second type :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    median<vector<double>::const_iterator,double>(begin, end);
    C'est pour ça qu'on précise souvent le type retour comme premier paramètre générique :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    template <class T,class It>
    T median(It begin, It end) {
    Ce qui te permet d'écrire simplement :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    median<double>(begin, end);

  3. #3
    Membre averti
    Profil pro
    professeur des universités à la retraite
    Inscrit en
    Août 2008
    Messages
    364
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : professeur des universités à la retraite

    Informations forums :
    Inscription : Août 2008
    Messages : 364
    Points : 439
    Points
    439
    Par défaut
    Je n'avais pas fait cet exercice, mais j'ai essayé vos deux propositions et ça marche très bien (sans modifier quoi que ce soit au makefile ou autre manip) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
     #include <iostream>
    #include <vector>
    #include <algorithm>
    #include <stdexcept>
     
    using namespace std;
     
    template < class T, class It>
    T median(It begin, It end) {
      if (begin == end) 
        throw domain_error("median of an empty container");
      sort(begin, end);
      --end;
      while (*begin < *end) {
        ++begin;
        --end;
      }
      return begin != end ? (*begin + *end) / 2 : *begin;
    }
     
    int main() { 
      vector<double> numbers; 
     
      numbers.push_back(1); 
      numbers.push_back(3);
      numbers.push_back(4);
      numbers.push_back(5); 
      numbers.push_back(2);
      numbers.push_back(6);
     
      vector<double>::iterator begin = numbers.begin(); 
      vector<double>::iterator end = numbers.end(); 
     
      double result = median<double>(begin, end); 
      cout << result << endl;
      return 0;
    }

  4. #4
    Membre averti
    Profil pro
    professeur des universités à la retraite
    Inscrit en
    Août 2008
    Messages
    364
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : professeur des universités à la retraite

    Informations forums :
    Inscription : Août 2008
    Messages : 364
    Points : 439
    Points
    439
    Par défaut
    @goran kajfes
    Cela dit je n'avais pas du tout compris l'exercice comme toi.

    L'énoncé disait ceci :

    Note that the various analysis functions we wrote in §6.2/110 share the same behavior;
    they differ only in terms of the functions they call to calculate the final grade. Write a template function, parameterized by the type of the grading function, and use that function to evaluate the grading schemes.
    Les 'various analysis functions' dont il est question me semblaient être
    p113 :
    - double median_analysis(const vector<Student_info& students);
    p. 115 :
    - double average_analysis(const vector<Student_info& students);
    qui en effet ont mêmes types d'arguments et même type de valeur de retour et aussi même code à la seule différence que l'une appelle, indirectement, la fonction grade_aux et l'autre la fonction average_grade, comme arguments de transform().

    Il me semblait que le template à trouver était une sorte de généralisation de ces deux fonctions (utilisées l'une et l'autre dans le main() de la page 114).
    Il y a aussi, il est vrai, optimistic_median_analysis p 116, mais son code est un peu différent et je ne sais pas trop si on pourrait aussi l'intégrer d'une manière ou d'une autre dans le même template.

  5. #5
    Membre régulier
    Profil pro
    Inscrit en
    Novembre 2005
    Messages
    128
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2005
    Messages : 128
    Points : 122
    Points
    122
    Par défaut
    @3DArchi: merci. Ça m'a été très utile. Je me suis servi de tes conseils pour résoudre d'autres exercices. Je crois que tout ce que tu as dit là n'est pas abordé dans le chapitre 8 (ou alors, je suis très tête-en-l'air). Pour tester la performance de cette fonction par rapport à son équivalent sans itérateur (en transmettant directement le vecteur en paramètre de la fonction), j'ai repris le fichier source main2.cc du chapitre 4 et ai appliqué l'exécutable sur un fichier de 10000 étudiants. Les performances sont à peu près identiques. J'ai du supprimer quelques const en paramètres de fonctions (obligé à cause du sort() de median() car on ne passe plus par une copie du vecteur).

    @ptyxs: mea culpa. Il s'agissait de l'exercice 8-3, et pas du 8-1.
    Il me semblait que le template à trouver était une sorte de généralisation de ces deux fonctions (utilisées l'une et l'autre dans le main() de la page 114).
    Pour résoudre ce dernier, j'avais simplifié les 3 fonctions analysis (ainsi, elles ne contiennent plus qu'une seule ligne de code) en créant une nouvelle fonction (je crois que c'était la solution d'un exo d'un autre chapitre). Ensuite j'ai ajouté des « template <class T> » sur ces 4 fonctions, T remplaçant le type double, et T étant automatiquement remplacé par double à la compilation quand la ligne median(grades) est « vue ». Du moins, c'est ma supposition.

  6. #6
    Membre régulier
    Profil pro
    Inscrit en
    Novembre 2005
    Messages
    128
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2005
    Messages : 128
    Points : 122
    Points
    122
    Par défaut
    Citation Envoyé par goran kajfes Voir le message
    Je crois que tout ce que tu as dit là n'est pas abordé dans le chapitre 8 (ou alors, je suis très tête-en-l'air).
    Mince, c'était dans la section Details. Mais ils ne parlent pas de l'ordre imposé des templates.

  7. #7
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 611
    Points
    30 611
    Par défaut
    Salut,
    Citation Envoyé par goran kajfes Voir le message
    Mince, c'était dans la section Details. Mais ils ne parlent pas de l'ordre imposé des templates.
    En fait, si tu ne définis pas de paramètre modèle par défaut, il n'y a pas vraiment d'ordre imposé...

    Par contre, si tu le fait, tu es soumis aux mêmes restrictions que lorsque tu donne une valeur par défaut un argument de fonction, à savoir qu'un argument peut avoir une valeur par défaut si c'est le dernier de la liste d'arguements (juste avant la fermeture de la parenthèse) ou s'il est suivi d'un argument par défaut, et que tu ne peux pas utiliser la valeur par défaut pour un argument si tu dois préciser la valeur d'un des arguments qui suivent.

    Comme il est à l'extrême limite possible de déterminer le type d'arguments réels que prendrait une fonction dont on a identifié le type de retour, mais qu'il est beaucoup plus difficile, sur base du type des arguments que prend une fonction, de déterminer le type de la valeur de retour, ce n'est donc que la pure logique qui fait que le type de la valeur de retour prend "naturellement" place en première position

    Pour te faire comprendre ce que j'ai écrit (qui n'est effectivement peut être pas très clair :aei, considère cette fonction template:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    template <class T, class U, class V>
    T foo(U u, V v)
    {
        /*...*/
    }
    Il s'agit d'une fonction qui manipule trois type distincts, nommés T, U et V, servant respectivement comme valeur de retrour, premier et second argument de la fonction.

    Chaque fois que tu voudrais l'utiliser, tu devrais indiquer explicitement les types respectifs pour T, U et V, sous une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    double d=foo<double, int*, long*>(mondouble, ptrint, ptrlong);
    mais cela pourrait tout aussi bien être (si les deux arguments sont sensés être des collections d'objets)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    MonObjet = foo<MonObjet, std::vector<Objet2>, std::list<Objet3> >(tab, lalist);
    Or, c'est là que tu te rend compte que le deuxième argument à fournir pourrait très bien être le même que le premier... Et que ce sera, selon toute vraisemblance, le plus souvent le cas.

    Tu serais alors tenté de donner une valeur par défaut au deuxième type template, sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    template <class T, class U, class V= U>
    T  foo(U u, V v)
    {
    }
    les deux appels que je t'ai présentés plus hauts restent valables, mais cela te permet, si la fonction doit renvoyer un MonObjet et prendre deux std::vector<Objet2> de simplifier l'appel en
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    MonObjet  res=foo(MonObjet, std::vector<Objet2> >(truc, tab1, tab2);
    Et c'est enfin là que tu te rend compte, que, le plus souvent, ta fonction renverra un objet de type T parce qu'elle a travaillé avec... des std::vector<T> pour les deux arguments (bien qu'il soit possible qu'il en soit autrement)...

    Pour que les différents appels présentés plus haut reste corrects, il faut toujours garder la possibilité de fournir trois types, mais, à bien y réfléchir, seul le premier présente un intérêt réel...

    Tu finirais donc en donnant une valeur par défaut au deuxième type template, sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    template <typename T,  class U=std::vector<T>, class V=U>
    T foo(U u, V v)
    {
        /*...*/
    }
    qui pourra être appelée sous la forme "simple" de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    MonObjet obj=foo<MonObjet>(truc, tab1, tab2);
    qui continuera malgré tout à travailler avec les appels précédents

    J'ai bien sur "déroulé" l'ensemble du raisonnement, pour bien te le faire comprendre, mais tu pourrais très bien arriver à la dernière solution "en une seule passe"
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  8. #8
    Rédacteur

    Avatar de Davidbrcz
    Homme Profil pro
    Ing Supaéro - Doctorant ONERA
    Inscrit en
    Juin 2006
    Messages
    2 307
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : Suisse

    Informations professionnelles :
    Activité : Ing Supaéro - Doctorant ONERA

    Informations forums :
    Inscription : Juin 2006
    Messages : 2 307
    Points : 4 732
    Points
    4 732
    Par défaut
    On peut s'en sortir sans rien expliciter avec la structure std::iterator_traits définie dans <iterator>.
    Exemple:


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
     
    #include <iostream>
    #include <iterator>
    #include <vector>
    #include <algorithm>
    #include <stdexcept>
     
    using namespace std;
     
    template <class It>
    typename std::iterator_traits<It>::value_type median(It begin,It end) {
      if (begin == end) 
        throw domain_error("median of an empty container");
      sort(begin, end);
      --end;
      while (*begin < *end) {
        ++begin;
        --end;
      }
      return begin != end ? (*begin + *end) / 2 : *begin;
    }
     
    int main() { 
      vector<double> numbers; 
     
      numbers.push_back(1); 
      numbers.push_back(3);
      numbers.push_back(4);
      numbers.push_back(5); 
      numbers.push_back(2);
      numbers.push_back(6);
     
      vector<double>::iterator begin = numbers.begin(); 
      vector<double>::iterator end = numbers.end(); 
     
      double result = median(begin, end); 
      cout << result << endl;
      return 0;
    }
    "Never use brute force in fighting an exponential." (Andrei Alexandrescu)

    Mes articles dont Conseils divers sur le C++
    Une très bonne doc sur le C++ (en) Why linux is better (fr)

  9. #9
    Membre régulier
    Profil pro
    Inscrit en
    Novembre 2005
    Messages
    128
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2005
    Messages : 128
    Points : 122
    Points
    122
    Par défaut
    Bonsoir.

    @koala01: c'est plus clair, merci.

    En fait les paramètres templates se comportent comme les paramètres normaux des fonctions: valeurs par défaut...

    @Davidbrcz: std::iterator_traits<It>::value_type: intéressant, merci.
    Quelle méthode est conseillée? ma_fonction<double>() ou value_type? La 2ème a au moins le mérite de ne pas donner une information en double lors de l'appel de la fonction, puisque le type double est une information qui est déjà transmise par les itérateurs. Donc c'est plus élégant, non?

    En tout cas, pour la réponse à l'exo, c'est la 1ère méthode qui était attendue (car iterator_traits n'a pas encore été vu dans le livre)

  10. #10
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 611
    Points
    30 611
    Par défaut
    Citation Envoyé par goran kajfes Voir le message
    Bonsoir.

    @koala01: c'est plus clair, merci.

    En fait les paramètres templates se comportent comme les paramètres normaux des fonctions: valeurs par défaut...
    à ceci près que tu n'es absolument pas tenu de respecter l'ordre dans lequel tu as déclaré les parmamètres template lorsque tu décide d'utiliser les types réels dans la déclaration ou la définition d'une fonction...
    un code proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    template <typename T1,typename T2, typename T3, typename T4>
    T3 foo(T2 a ,T4 b, T1 c)
    {
    }
    est tout à fait valide
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  11. #11
    Alp
    Alp est déconnecté
    Expert éminent sénior

    Avatar de Alp
    Homme Profil pro
    Inscrit en
    Juin 2005
    Messages
    8 575
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations forums :
    Inscription : Juin 2005
    Messages : 8 575
    Points : 11 860
    Points
    11 860
    Par défaut
    Citation Envoyé par koala01 Voir le message
    à ceci près que tu n'es absolument pas tenu de respecter l'ordre dans lequel tu as déclaré les parmamètres template lorsque tu décide d'utiliser les types réels dans la déclaration ou la définition d'une fonction...
    un code proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    template <typename T1,typename T2, typename T3, typename T4>
    T3 foo(T2 a ,T4 b, T1 c)
    {
    }
    est tout à fait valide
    C'est tout à fait valide mais c'est dommage de ne pas profiter de la déduction des arguments dans l'ordre qui fait en sorte que l'on a le moins à en spécifier.
    Il faut mettre en premier T1 celui que l'on est obligé de spécifier : le type de retour,et les autres après, car ils sont automatiquement déduits.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    template <typename T1, typename T2, typename T3, typename T4>
    T1 foo(T2 a, T3 b, T4 c)
    {
     
    }
    Bref, cf le premier message de 3DArchi dans cette même discussion.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Objet existant reconnu dans une fonction mais pas dans une autre
    Par Jiyuu dans le forum Général Python
    Réponses: 0
    Dernier message: 20/09/2011, 19h19
  2. Probleme CSS: Bon affichage menu sur Firefox mais pas sur IE7
    Par jisko42 dans le forum Mise en page CSS
    Réponses: 2
    Dernier message: 08/02/2010, 16h08
  3. Réponses: 1
    Dernier message: 03/06/2009, 20h12
  4. paramètre string fonction -> fonctionne pas toujours?!
    Par francoisvba dans le forum Macros et VBA Excel
    Réponses: 24
    Dernier message: 12/12/2008, 19h08
  5. Réponses: 5
    Dernier message: 02/10/2006, 20h24

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo