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] surcharge operateur de flux, héritage et template


Sujet :

Langage C++

  1. #1
    r0d
    r0d est déconnecté
    Expert éminent

    Homme Profil pro
    tech lead c++ linux
    Inscrit en
    Août 2004
    Messages
    4 262
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : tech lead c++ linux

    Informations forums :
    Inscription : Août 2004
    Messages : 4 262
    Points : 6 680
    Points
    6 680
    Billets dans le blog
    2
    Par défaut [template] surcharge operateur de flux, héritage et template
    Bien le bonjour tout le monde,

    j'ai un petit soucis et je cherche la façon la plus élégante de le résoudre.

    J'ai une classe Mere, que je pourrais présenter comme suit, et son opérateur d'indirection de flux:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    class Mere
    {
    friend std::ostream & operator << ( std::ostream & ostr, const Mere & mere );
     
    public:
       std::string name;
       int value;
    };
    Il se trouve que, les spécifications ayant changées, je dois implémenter différents comportements pour cette classe Mere. J'ai donc décidé d'utiliser le pattern template method et j'ai créé 2 classes Filles qui héritent de Mere et qui possèdent les même attributs. Seules quelques fonctions membres changent (c'est un cas typique de spécialisation):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    class Fille1 : public Mere
    { // code };
     
    class Fille2 : public Mere
    { // code };
    Maintenant ça se complique: j'ai une classe conteneur template, qui est vouée à prendre en paramètre template une des deux classes filles (j'ai fais comme ça car je suis sûr que les specs vont encore changer et qu'il va me falloir implémenter d'autres classes FilleX):

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    template <class TypeElement> // TypeElement = Fille1 ou Fille2
    class TableauDeMere
    {
    // ici tout mon code. c'est un gros fourre-tout ce conteneur.
    private:
       std::set<TypeElement, MonFoncteurDeComparaison> m_leSet;
    }
    Le décor est planté, voilà le problème: je n'arrive pas à implémenter mon opérateur d'indirection de flux pour mon TableauDeMere.

    J'ai essayé ça:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    template <class TypeElement> // TypeElement = Fille1 ou Fille2
    class TableauDeMere
    {
       friend std::ostream & operator << ( std::ostream & ostr, const TableauDeMere<TypeElement> & ad );
    //...
    }
    
    template <class TypeElement>
    std::ostream & operator << ( std::ostream & ostr, const TableauDeMere<TypeElement> & ad )
    {
    	std::copy( ad.m_leSet.begin(), ad.m_leSet.end(), std::ostream_iterator<Mere>(ostr, "\n"));
    // ici, Mere me parait le plus logique puisque je sais que quelque soit le type de TypeElement, il héritera de Mere. Et je veux utiliser l'operateur << de la classe Mere justement
    	return ostr;
    }
    Le code ci-dessus compile si je n'utilises pas l'operateur << de TableauDeMere. Lorsque je l'utilise (en essayant d'afficher un TableauDeMere<Fille1> par exemple), le compilo (j'utilise visual 2005 express ici) n'arrive pas à résoudre le typage:
    error LNK2019: unresolved external symbol "class std::basic_ostream<char,struct std::char_traits<char> > & __cdecl operator<<(class std::basic_ostream<char,struct std::char_traits<char> > &,class TableauDeMere<class Fille1> const &)" (??6@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@std@@AAV01@ABV?$TableauDeMere@VFille1@@@@@Z) referenced in function _main
    Voyez-vous mon problème? Voyez-vous une solution élégante?
    « L'effort par lequel toute chose tend à persévérer dans son être n'est rien de plus que l'essence actuelle de cette chose. »
    Spinoza — Éthique III, Proposition VII

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Salut,

    As tu, tout bêtement, implémenté l'opérateur << pour la classe mère idéalement, en te basant sur l'entrée de la FAQ qui traite de l'affichage d'objet polymorphes...

    De prime abord, c'est en effet au niveau de l'opérateur << des classes dérivées qu'il y a une absence à combler
    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

  3. #3
    r0d
    r0d est déconnecté
    Expert éminent

    Homme Profil pro
    tech lead c++ linux
    Inscrit en
    Août 2004
    Messages
    4 262
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : tech lead c++ linux

    Informations forums :
    Inscription : Août 2004
    Messages : 4 262
    Points : 6 680
    Points
    6 680
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Salut,

    As tu, tout bêtement, implémenté l'opérateur << pour la classe mère
    Vivi: déclarée en amie de la classe Mere et implémentée.

    Citation Envoyé par koala01 Voir le message
    idéalement, en te basant sur l'entrée de la FAQ qui traite de l'affichage d'objet polymorphes...
    Vi, j'avais vu cette faq, mais elle ne répond pas à mon problème. En fait, moi je ne veux qu'un seul operateur << pour toutes mes classes filles, qui sera donc implémenté dans la classe mère.

    Citation Envoyé par koala01 Voir le message
    De prime abord, c'est en effet au niveau de l'opérateur << des classes dérivées qu'il y a une absence à combler
    Ben vi, mais où? (et surtout, pourquoi?)
    « L'effort par lequel toute chose tend à persévérer dans son être n'est rien de plus que l'essence actuelle de cette chose. »
    Spinoza — Éthique III, Proposition VII

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par r0d Voir le message
    Vivi: déclarée en amie de la classe Mere et implémentée.
    désolé... la distraction aidant, c'était une solution "simple"

    ... mais je suis très distrait
    Vi, j'avais vu cette faq, mais elle ne répond pas à mon problème. En fait, moi je ne veux qu'un seul operateur << pour toutes mes classes filles, qui sera donc implémenté dans la classe mère.
    Evidemment, si la classe mere dispose de toutes les informations pour provoquer l'affichage correct...
    Ben vi, mais où? (et surtout, pourquoi?)
    La seule solution que j'envisage, c'est que, s'agissant d'une classe template, l'implémentation de l'opérateur << pour ta classe TableauDeMere
    n'est pas accessibe avec la seule inclusion du fichier d'en-tête dans lequel elle est définie...
    [EDIT] quand au pourquoi, il est simple: l'implémentation doit être accessible au départ du fichier d'en-tête de manière à permettre au compilateur de l'adapter selon le type de l'objet réellement utilisé
    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

  5. #5
    Membre actif
    Profil pro
    Inscrit en
    Août 2007
    Messages
    190
    Détails du profil
    Informations personnelles :
    Localisation : France, Maine et Loire (Pays de la Loire)

    Informations forums :
    Inscription : Août 2007
    Messages : 190
    Points : 219
    Points
    219
    Par défaut
    Salut,

    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
    template <class TypeElement> // TypeElement = Fille1 ou Fille2
    class TableauDeMere;
    
    template <class TypeElement>
    std::ostream & operator << ( std::ostream &, const TableauDeMere<TypeElement> &);
    
    template <class TypeElement> // TypeElement = Fille1 ou Fille2
    class TableauDeMere
    {
       friend std::ostream & operator << <>( std::ostream & ostr, const TableauDeMere<TypeElement> & ad );
    //...
    }
    
    template <class TypeElement>
    std::ostream & operator << ( std::ostream & ostr, const TableauDeMere<TypeElement> & ad )
    {
    	std::copy( ad.m_leSet.begin(), ad.m_leSet.end(), std::ostream_iterator<Mere>(ostr, "\n"));
    // ici, Mere me parait le plus logique puisque je sais que quelque soit le type de TypeElement, il héritera de Mere. Et je veux utiliser l'operateur << de la classe Mere justement
    	return ostr;
    }
    Ca devrait résoudre ton problème. Le fait de rajouter <> (j'aurais pu mettre <TypeElement>) permet de signaler au compilateur que tu souhaites qu'une instance de ta classe TableauDeMere pour un type T soit amie avec l'instance de l'operateur << pour le même type T.

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Le plus étrange dans l'histoire, c'est qu'un code proche de
    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
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    class Mere
    {
    	public:
    		Mere(int i): i(i){}
    		virtual ~Mere(){}
    		friend ostream& operator<<(ostream& ofs, const Mere& m)
    		{
    		    ofs<<m.i<<" ok";
    		    return ofs;
    		}
    		bool operator <(const Mere& m) const
    		{return i<m.i;}
    	protected:
     
    	private:
    		int i;
    };
    class Fille : public Mere
    {
    	public:
    		Fille(int i):Mere(i){}
    		~Fille(){}
    	protected:
     
    	private:
     
    };
    template <class T>
    class TMySet
    {
        public:
        void insert(const T& toadd)
        {
            s.insert( toadd);
        }
        friend ostream& operator<<(ostream& ofs, const TMySet& m)
        {
            copy( m.s.begin(), m.s.end(), ostream_iterator<T>(ofs, "\n"));
            return ofs;
        }
        set<T> s;
    };
     
    int main(int argc, char* argv[])
    {
        TMySet<Fille> s;
        for(int i = 0;i<10; ++i)
            s.insert(Fille(i));
        cout<<s;
    	return 0;
    }
    (tout dans un seul fichier ) fonctionne sans problème chez moi (sous réserve d'inclure <iterator>...

    J'ai même essayé en mettant chaque classe dans un fichier séparé, et je n'ai eu aucun problème.

    Définitivement, j'ai l'impression que tu auras implémenté l'opérateur << de ta classe TableauDeMere dans un fichier qui n'est pas inclus dans le fichier d'en-tête correspondant.

    En effet, il n'y a que dans la configuration suivante:
    fichier "Mere.hpp"
    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
    #ifndef MERE_HPP_INCLUDED
    #define MERE_HPP_INCLUDED
    #include <iostream>
    using namespace std;
    class Mere
    {
        public:
            Mere(int i): i(i){}
            virtual ~Mere(){}
            friend ostream& operator<<(ostream& ofs, const Mere& m)
            {
                ofs<<m.i<<" ok";
                return ofs;
            }
            bool operator <(const Mere& m) const
            {return i<m.i;}
        protected:
        private:
            int i;
    };
    #endif // MERE_HPP_INCLUDED
    fichier Fille.hpp
    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
     
    #ifndef FILLE_HPP_INCLUDED
    #define FILLE_HPP_INCLUDED
    #include "Mere.hpp"
     
    class Fille : public Mere
    {
        public:
            Fille(int i):Mere(i){}
    		~Fille(){}
    	protected:
     
    	private:
     
    };
    #endif // FILLE_HPP_INCLUDED
    fichier "TMySet.hpp
    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
    #ifndef TMYSET_HPP_INCLUDED
    #define TMYSET_HPP_INCLUDED
    #include <set>
    #include <iostream>
    #include <iterator>
    using namespace std;template <class T>
    class TMySet
    {
        public:
            void insert(const T& toadd)
            {
                s.insert( toadd);
            }
            template<class I>
            friend ostream& operator<< (ostream& ofs, const TMySet<I>& m);
            set<T> s;
    };
    #endif // TMYSET_HPP_INCLUDED
    fichier TMySet.cpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    #include "TMySet.hpp"
    template < class I>
    ostream& operator<< (ostream& ofs,TMySet<I>&m)
    {
        copy( m.s.begin(), m.s.end(), ostream_iterator<I>(ofs, "\n"));
        return ofs;
    }
    fichier main.cpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    #include "Fille.hpp"
    #include "TMySet.hpp"
     
    int main(int argc, char* argv[])
    {
        TMySet<Fille> s;
        for(int i = 0;i<10; ++i)
            s.insert(Fille(i));
        cout<<s;
    	return 0;
    }
    que j'arrive à reproduire une erreur similaire...
    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

  7. #7
    Membre actif
    Profil pro
    Inscrit en
    Août 2007
    Messages
    190
    Détails du profil
    Informations personnelles :
    Localisation : France, Maine et Loire (Pays de la Loire)

    Informations forums :
    Inscription : Août 2007
    Messages : 190
    Points : 219
    Points
    219
    Par défaut
    @Koala: As-tu lu mon post ?

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Oui, mais le problème n'est pas là...

    Une erreur "undefined reference to (something)" est défintivement une erreur rencontrée à... l'édition de liens.

    Cela signifie que le compilateur fait correctement son travail, à l'exception d'un seul fait: il ne crée pas le code correspondant à l'implémentation d'une méthode qu'il connait pourtant.

    Dés lors, avec les fichiers objet dont l'éditeur de liens dispose, il n'est pas en mesure... de trouver l'adresse à laquelle les appels (propres à la spécialisation du template) doivent être reliés.

    C'est pour cela que j'insiste sur le fait que l'implémentation d'une fonction membre d'une classe template doit impérativement être accessible depuis le fichier d'en-tête dans lequel la classe est définie, ainsi que sur le fait faire autrement est la seule possibilité pour reproduire l'erreur
    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

  9. #9
    Membre actif
    Profil pro
    Inscrit en
    Août 2007
    Messages
    190
    Détails du profil
    Informations personnelles :
    Localisation : France, Maine et Loire (Pays de la Loire)

    Informations forums :
    Inscription : Août 2007
    Messages : 190
    Points : 219
    Points
    219
    Par défaut
    Le problème c'est que la déclaration d'amitié n'est pas correcte (enfin si elle est correcte mais elle n'a pas l'effet escompté par r0d). Je suppose que r0d sait que la définition d'une fonction template doit être présente dans chaque unité de compilation qui l'utilise et donc pour moi la raison que tu invoques pour expliquer l'erreur de link n'est exacte.

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par Montag Voir le message
    Le problème c'est que la déclaration d'amitié n'est pas correcte (enfin si elle est correcte mais elle n'a pas l'effet escompté par r0d). Je suppose que r0d sait que la définition d'une fonction template doit être présente dans chaque unité de compilation qui l'utilise et donc pour moi la raison que tu invoques pour expliquer l'erreur de link n'est exacte.
    Je crois qu'il faudrait attendre les réponses de rûd avant d'aller plus loin.

    En effet, je reste sur ma position en ce qui concerne l'inaccessibilité de la méthode, et j'en veux pour preuve le fait que, si elle était implémentée dans un fichier .tpp qui lui-même est inclus dans le fichier d'en-tête ad-hoc, l'erreur disparaitrait "comme par magie" (même si ce n'est que pure logique )
    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
    Membre actif
    Profil pro
    Inscrit en
    Août 2007
    Messages
    190
    Détails du profil
    Informations personnelles :
    Localisation : France, Maine et Loire (Pays de la Loire)

    Informations forums :
    Inscription : Août 2007
    Messages : 190
    Points : 219
    Points
    219
    Par défaut
    Donc, si je te suis, ce code ne doit poser aucun problème
    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
    #include <iostream>
     
    template<typename T>
    class A
    {
      friend std::ostream& ::operator<<(std::ostream&, A<T> const&);
    };
     
    template<typename T>
    std::ostream& operator<<(std::ostream& os, A<T> const& a)
    {
      return os;
    }
     
    int main()
    {
      A<int> a;
      std::cout << a << std::endl;
      return 0;
    }
    ?

  12. #12
    r0d
    r0d est déconnecté
    Expert éminent

    Homme Profil pro
    tech lead c++ linux
    Inscrit en
    Août 2004
    Messages
    4 262
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : tech lead c++ linux

    Informations forums :
    Inscription : Août 2004
    Messages : 4 262
    Points : 6 680
    Points
    6 680
    Billets dans le blog
    2
    Par défaut
    oui hum heu, excusez-moi, j'ai beaucoup de boulot en ce moment. Enfin, pas plus que d'habitude, mais les délais sont serrés.

    Vous avez écris beaucoup de choses. Je vais lire tout ça et je vous dit
    « L'effort par lequel toute chose tend à persévérer dans son être n'est rien de plus que l'essence actuelle de cette chose. »
    Spinoza — Éthique III, Proposition VII

  13. #13
    r0d
    r0d est déconnecté
    Expert éminent

    Homme Profil pro
    tech lead c++ linux
    Inscrit en
    Août 2004
    Messages
    4 262
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : tech lead c++ linux

    Informations forums :
    Inscription : Août 2004
    Messages : 4 262
    Points : 6 680
    Points
    6 680
    Billets dans le blog
    2
    Par défaut
    Bon, finalement cet exemple m'est très instructif. Je me rend compte qu'il y a plusieurs choses que je n'avais pas compris, et que je ne comprend toujours pas à l'heure où j'écris ces lignes (mais ça ne va pas durer )

    Donc oui, je sais bien que toute définition d'une fonction template doit être présente dans son unité de compilation. En fait, dans le code qui m'a posé problème (et qui est vraiment gros, c'est pourquoi je ne l'ai pas posté ici), j'inclus le .cpp à la fin du .h.

    Sinon, le premier exemple que tu donnes (celui dans un seul fichier), koala, compile effectivement. Mais il ne compile plus si je sors l'opérateur de flux de la classe myset (même en ne conservant qu'un seul fichier).

    J'ai fais ça:
    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
    template <class T>
    class myset
    {
    //...
    	friend ostream& operator<<(ostream& ofs, const myset& m); // juste je déclare
    //...
    };
     
    // et je le défini en dehors (mais dans le même fichier)
    template <typename T>
    ostream& operator<<(ostream& ofs, const myset<T>& m)
    {
    	copy( m.s.begin(), m.s.end(), ostream_iterator<T>(ofs, "\n"));
    	return ofs;
    }
    Et là c'est la catastrophe, je retombe sur la même erreur que je vous ait décrit dans mon premier post (qui est bien une erreur de link).

    Et là, ce que je ne comprend pas c'est:
    1/ Pourquoi ça ne compile plus quand je sors ce m@udit operateur?
    2/ Je ne comprend pas ce que c'est que cette fonction friend à l'intérieur même de la classe, comme tu a fais koala. Par curiosité, j'ai enlevé le mot-clé friend et ça ne compile plus. A quoi sert-il ici?
    « L'effort par lequel toute chose tend à persévérer dans son être n'est rien de plus que l'essence actuelle de cette chose. »
    Spinoza — Éthique III, Proposition VII

  14. #14
    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
    "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)

  15. #15
    Membre actif
    Profil pro
    Inscrit en
    Août 2007
    Messages
    190
    Détails du profil
    Informations personnelles :
    Localisation : France, Maine et Loire (Pays de la Loire)

    Informations forums :
    Inscription : Août 2007
    Messages : 190
    Points : 219
    Points
    219
    Par défaut
    Citation Envoyé par r0d Voir le message
    Bon, finalement cet exemple m'est très instructif. Je me rend compte qu'il y a plusieurs choses que je n'avais pas compris, et que je ne comprend toujours pas à l'heure où j'écris ces lignes (mais ça ne va pas durer )

    Donc oui, je sais bien que toute définition d'une fonction template doit être présente dans son unité de compilation. En fait, dans le code qui m'a posé problème (et qui est vraiment gros, c'est pourquoi je ne l'ai pas posté ici), j'inclus le .cpp à la fin du .h.

    Sinon, le premier exemple que tu donnes (celui dans un seul fichier), koala, compile effectivement. Mais il ne compile plus si je sors l'opérateur de flux de la classe myset (même en ne conservant qu'un seul fichier).

    J'ai fais ça:
    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
    template <class T>
    class myset
    {
    //...
    	friend ostream& operator<<(ostream& ofs, const myset& m); // juste je déclare
    //...
    };
     
    // et je le défini en dehors (mais dans le même fichier)
    template <typename T>
    ostream& operator<<(ostream& ofs, const myset<T>& m)
    {
    	copy( m.s.begin(), m.s.end(), ostream_iterator<T>(ofs, "\n"));
    	return ofs;
    }
    Et là c'est la catastrophe, je retombe sur la même erreur que je vous ait décrit dans mon premier post (qui est bien une erreur de link).

    Et là, ce que je ne comprend pas c'est:
    1/ Pourquoi ça ne compile plus quand je sors ce m@udit operateur?
    2/ Je ne comprend pas ce que c'est que cette fonction friend à l'intérieur même de la classe, comme tu a fais koala. Par curiosité, j'ai enlevé le mot-clé friend et ça ne compile plus. A quoi sert-il ici?
    J'ai l'impression de prêcher dans le désert. As-tu lu mes précédents posts ? Il me semble t'avoir donner la solution (mais il est vrai que l'explication n'est pas super claire).

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Reprenons depuis le début:

    Le mot clé friend va avoir deux résultats:

    Le premier, c'est de "sortir" la fonction de la portée de la classe
    Le second (le plus observable), c'est de permettre à cette fonction qui "ne fait pas partie de la classe" d'accéder aux éléments qui ne sont pas exposés au reste du code (tout ce qui n'est pas d'accessibilité publique).

    Ce qui se passe, c'est que lorsque tu implémente le comportement d'une fonction amie directement au sein de la classe, la déclaration a beau être "hors de la portée de la classe", l'implémentation se fait... dans la protée de la classe
    Dés lors, le compilateur sait que le deuxième argument (celui qui est template) est ... template, justement.

    Par contre, si tu sors l'implémentation de la fonction de la classe qui la déclare amie, il faut indiquer au moment de la déclaration qu'il s'agit d'une fonction qui va "jouer" avec des paramètres template (rendre la fonction template ) parce que, justement, il ne s'agit plus d'une méthode de la classe

    C'est la raison pour laquelle, soit tu fais l'implémentation directement au sein de la classe, et un simple
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    template <class T>
    class MaClass
    {
        friend ostream& operator<<(ostream& os, const MaClass& mc)
        {
             /*affichage des membres qui nous intéressent */
            return os;
        }
    }
    suffit, soit, il faut que la déclaration de la fonction indique ... qu'il s'agit d'une fonction template (et que l'implémentation soit considérée comme faisant partie du fichier d'en-tête) sous une forme proche de
    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
    template <class T>
    class MaClass
    {
        friend template<class B> // on ne peut malheureusement plus utiliser T
        ostream& operator<< (ostream& , const MaClass<B> &);
    }
    /* soit dans le fichier d'en-tete, soit dans un fichier inclus dans le fichier
     * d'en-tete
     */
    template <class B>
    ostream& operator<<(ostream& os, const MaClass<B>& mc)
    {
        /* affichage des membres qui nous intéressent */
        return os;
    }
    @ Montag>> effectivement, le code que tu présentais quelques messages plus tot pose problème (et, effectivement, nous sort une erreur d'édition de liens identiques )...
    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

  17. #17
    Membre actif
    Profil pro
    Inscrit en
    Août 2007
    Messages
    190
    Détails du profil
    Informations personnelles :
    Localisation : France, Maine et Loire (Pays de la Loire)

    Informations forums :
    Inscription : Août 2007
    Messages : 190
    Points : 219
    Points
    219
    Par défaut
    @Koala: Sauf que la solution que tu proposes a l'inconvénient de rendre une instance quelconque de la classe MaClass amie avec toutes les instances de l'operator<<. Or ce n'est pas ce que souhaite r0d (si j'ai bien compris). Lui il veut que, par exemple, MaClass<int> soit amie avec operator<< <int> et c'est tout. La solution que j'ai proposé permet de réaliser cela.

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par Montag Voir le message
    @Koala: Sauf que la solution que tu proposes a l'inconvénient de rendre une instance quelconque de la classe MaClass amie avec toutes les instances de l'operator<<. Or ce n'est pas ce que souhaite r0d (si j'ai bien compris). Lui il veut que, par exemple, MaClass<int> soit amie avec operator<< <int> et c'est tout. C'est ce que la solution que j'ai proposé permet de faire.
    Non, ce que je propose, c'est exactement ce que rûd demande, et dont j'ai donné le code au 6eme message de cette discussion, à savoir:
    • surcharger l'opérateur << pour la classe Mere
    • surcharger l'opérateur << pour la collection (template) d'objets.

    Cela signifie qu'il y a amitié avec:
    • la classe Mere (et uniquement celle là, l'amitié n'étant pas héritée)
    • la collection personnelle (que j'ai nommé TMySet dans le code)

    Maintenant, il est vrai que l'opérateur << peut s'appliquer aussi bien à un flux console qu'à n'importe quel autre flux "sortant", mais si rûd avait voulu qu'il ne s'applique qu'à la console ou à un fichier, il l'aurait précisé

    Les code que j'ai donné dans le 16eme message n'ont été donnés qu'à titre d'exemple afin de lui permettre de comprendre les deux possibilités qui s'offraient à lui pour gérer l'amitié de fonction au sein d'une classe template
    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

  19. #19
    Membre actif
    Profil pro
    Inscrit en
    Août 2007
    Messages
    190
    Détails du profil
    Informations personnelles :
    Localisation : France, Maine et Loire (Pays de la Loire)

    Informations forums :
    Inscription : Août 2007
    Messages : 190
    Points : 219
    Points
    219
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Non, ce que je propose, c'est exactement ce que rûd demande, et dont j'ai donné le code au 6eme message de cette discussion, à savoir:

    surcharger l'opérateur pour la classe Mere
    surcharger l'opérateur pour la collection (template) d'objets.

    Les code que j'ai donné dans le 16eme message n'ont été donnés qu'à titre d'exemple afin de lui permettre de comprendre les deux possibilités qui s'offraient à lui pour gérer l'amitié de fonction au sein d'une classe template
    Non ce que tu proposes n'est, à mon avis, pas la meilleure solution (je parle de celle de ton post précédent). Certes elle fonctionne mais a un inconvénient majeur (cf mon précédent post). J'aimerais savoir pourquoi tu ne veux admetttre que ce que j'ai proposé dans un tout premier post règle le problème sans avoir à modifier énormément le code de r0d (il faut juste ajouter <>)

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Dis toi bien que, à partir du moment où tu decide de surcharger un opérateur pour une collection que tu as pris la peine de rendre template, c'est pour que ce comportement s'applique quel que soit le contenu pour lequel tu déclarera une instance de cette collection...

    Il est, effectivement, possible d'envisager une spécialisation particulière, mais cela fait partie d'un autre débat

    Comme l'amitié n'est pas héritée, la notion d'amitié du flux ne s'applique qu'à la composante "classe de base" des classes dérivées.

    En effet, et c'était une des specs de rûd :
    Vi, j'avais vu cette faq, mais elle ne répond pas à mon problème. En fait, moi je ne veux qu'un seul operateur << pour toutes mes classes filles, qui sera donc implémenté dans la classe mère.
    et, même si tu mettais d'autres membres dans les classes dérivées, tu te ferais "jeter" par le compilateur si tu essayais d'y accéder avec ta fonction ostream& operator<< (ostream&, const Mere&) (vu qu'une classe de base ne sait rien de ses descendants)
    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

Discussions similaires

  1. souci classe template et surcharge operateur
    Par loicounet dans le forum Langage
    Réponses: 8
    Dernier message: 28/04/2008, 15h01
  2. Réponses: 2
    Dernier message: 05/01/2008, 23h39
  3. Optimisation Listechainé avec template et operateur surchargé.
    Par Alain Defrance dans le forum Langage
    Réponses: 8
    Dernier message: 29/12/2007, 18h25
  4. [Template] Surcharge operateur
    Par juls64 dans le forum C++
    Réponses: 7
    Dernier message: 04/05/2007, 19h35
  5. surcharge operateur delete et héritage
    Par Hervé dans le forum C++
    Réponses: 5
    Dernier message: 29/03/2006, 13h59

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