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

 C++ Discussion :

vector<Mere> et vector<Fille> même combat


Sujet :

C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre confirmé
    Homme Profil pro
    Développeur Web
    Inscrit en
    Décembre 2006
    Messages
    126
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Décembre 2006
    Messages : 126
    Par défaut vector<Mere> et vector<Fille> même combat
    Bonjour,

    Dans mon programme, j'utilise des std::vector de classes qui héritent toutes d'une même classe mère comme suit

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    class Symbol {...};
    class Polygon : public Symbol {...};
    class Line : public Symbol{...};
    j'ai ailleurs dans le programme 2 vecteurs :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    std::vector<Line> _vectLine;
    std::vector<Polygon> _vectPolygon;
    je souhaite écrire une méthode qui s'appelle aussi-bien sur un vector<Line> que sur un vector<Polygon>, donc j'écris
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    void doSomething(std::vector<Symbol> vect);
    seulement voilà, lors de l'appel de ma méthode, il me dit qu'il ne peut pas convertir les vecteurs ...
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    std::vector<Line> _vect;
    doSomething(_vect);
    error C2440: 'cast de type'*: impossible de convertir de 'std::vector<_Ty>' en 'std::vector<_Ty>'
    Je suis sûr que la chose doit être possible mais la syntaxe m'échappe.
    J'ai pas envie de devoir déclarer tout mes vecteurs de classe fille comme des vecteurs de classe mère ...

    d'avance je vous remercie de votre aide

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Salut,

    C'est normal...

    Il y a deux raisons à cela:
    • D'abord, vectro<Type1> représente un type différent de vector<Type2>
    • Ensuite, pour pouvoir profiter du polymorphisme, il faut impérativement manipuler des références ou des pointeurs.

    (au passage, tu devrais transmettre ton vecteur par référence, éventuellement constante si la fonction ne dois pas modifier le vecteur, mais cela t'éviterait des copies inutiles et certainement très gourmandes en ressources

    Par contre, si tu avais un vecteur de pointeurs sur Symbol, tu pourrait faire cohabiter des lignes et des polygones, avec la restriction que tu n'aurais accès qu'aux fonctions membres déclarées pour le type Symbol...

    Si tel n'est pas ton souhait, Un petit coup de template devrait te venir en aide

    Quelque chose proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    template<class T>
    void doSomething(std::vector<T> vect);
    (non testé, il manque peut être un typename quelque part ) devrait pouvoir faire l'affaire
    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
    Membre expérimenté Avatar de Nogane
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    241
    Détails du profil
    Informations personnelles :
    Âge : 44
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 241
    Par défaut
    Même réponse que koala01, les template vont te permettre de le faire.

    Pour aller plus loin dans les template, et pour être encore un peut plus dans l'"esprit STL", on pourrai faire ca:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    template<iterator T>
    void doSomething(iterator iter, iterator end)
    {
      for(/**/; iter != end; ++iter)
        doSomething(*iter);
    }
    Comme ca, même si c'est pas un vector, ca marche quand même.

    Et sinon, si t'as pas envie de faire des template, et que tu est SUR que tu utilise un vector, un boost::array, ou un tableau, tu peut faire comme ca:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    void doSomething(Symbol *iter, Symbol *end)
    {
      for(/**/; iter != end; ++iter)
        doSomething(*iter);
    }
    EDIT: Je viens de tilter que la deuxième solution ne marchera pas car quand on fera ++iter le pointeur avancera de sizeof(Symbol) octet, ce qui peut être faux.
    En fait il fraudai être sur un tableau de pointeur et passer a doSomething des pointeurs de pointeurs mais ca commence a être un peut alambiqué.

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    A titre tout à fait personnel, je n'aime pas trop les boucle pour amputée d'une de leur valeur...

    Pour suivre ton inspiration, je ferais donc plutôt un truc proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    template<class T>
    void doSomething(T it, T end)
    {
        while(it!=end)
        {
            (*it).laFonctionVoulue();
            ++it;
        }
    }
    voire, pour pouvoir utiliser les fonctions utilisant des écarts (for_each et autres), de créer un foncteur prenant la forme de

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    template<class T>
    struct doSomething
    {
     
        void operator()(T /* const */ & value) /* const*/
        {
            value.laFonctionAAppeler();
        }
    };
    Il est d'ailleurs à noter que, l'idéal est (aussi) de ne jamais exposer un conteneur si tu peux faire sans et d'essayer autant que possible de n'exposer que les itérateurs de débuts et de fin sur ce conteneur.

    En effet, le jour où tu souhaitera passer d'un std::vector à un std::list ou meme à un std::map(par exemple), la solution qui passe par un foncteur restera tout à fait valide, et même la boucle for_each (ou de toute autre fonction manipulant des écarts) ne devra pas être modifiée.
    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 très actif Avatar de metagoto
    Profil pro
    Hobbyist programmateur
    Inscrit en
    Juin 2009
    Messages
    646
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Hobbyist programmateur

    Informations forums :
    Inscription : Juin 2009
    Messages : 646
    Par défaut
    Quand il s'agit juste d'appeler un membre sur chaque élément d'un container, je préfère utiliser directement std::mem_fun plutôt que de (re)créer un foncteur pour ça.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    std::for_each(v.begin(), v.end(), std::mem_fun(&Symbol::virtual_fun));
    Par rapport à Nogane
    EDIT: Je viens de tilter que la deuxième solution ne marchera pas car quand on fera ++iter le pointeur avancera de sizeof(Symbol) octet, ce qui peut être faux.
    En fait il fraudai être sur un tableau de pointeur et passer a doSomething des pointeurs de pointeurs mais ca commence a être un peut alambiqué.
    En fait, si ça va fonctionner. koala01 a une solution plus générique.
    Par contre, il faudra passer par std::iterator_traits<T> si on veut connaître le type déréférencer ou la distance entre les élems.

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Citation Envoyé par metagoto Voir le message
    Par rapport à Nogane
    En fait, si ça va fonctionner. koala01 a une solution plus générique.
    Par contre, il faudra passer par std::iterator_traits<T> si on veut connaître le type déréférencer ou la distance entre les élems.
    Effectivement, la fonction pourrait se présenter sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    template<class T, class iter=std::iterator_traits<T> >
    void foo(iter it, iter end)
    {
        while(it!=end)
        {
            (*it).laFonctionQuiVaBien();
            ++it;
        }
    }
    mais, le jour où tu voudrais faire en sorte qu'elle fonctionne avec une std::map (ou autre container associatif utilisant des tuple), il faudra créer une fonction dont le nom diffère et qui serait proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    template<class K, class V, class iter=std::iterator_traits<typename std::pair<K const,V> > >
    void bar(iter it, iter end)
    {
        while(it!=end)
        {
            /*acces éventuel à la clé */
            std::cout<<(*it).first;
     
            (*it).second.laFonctionQuiVaBien();
            ++it;
        }
    }
    L'un dans l'autre, la solution passant par un foncteur me semble bien plus pertinente et ce, d'autant plus que, comme je l'ai fait remarquer:
    1. il est préférable d'utiliser les fonctions qui manipulent des écart [debut,fin) plutôt que des boucle
    2. il est préférable d'éviter d'exposer ce qui n'a pas besoin de l'être (si on peut éviter d'exposer toute fonction propre au container qui représente le membre d'une classe, c'est toujours ça de gagné )
    3. le principe de la délégation des taches semble indiquer que ce serait à l'objet contenu de travailler lorsque l'on parcoure le conteneur.

    Au sujet du troisième point, il serait d'ailleurs possible d'avoir un foncteur imbriqué, éventuellement déclaré ami de l'objet :
    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
     
    class Base
    {
            friend class FoncteurBase;
        public:
            virtual ~Base(){}
            struct FoncteurBase
            {
                void operator()(Base /*const*/ & b) /*const*/
                {
                    std::cout<<"Base"<<std::endl;
                }
            };
    };
    class Derivee : public Base
    {
     
            friend class FoncteurDerivee;
        public:
            virtual ~Derivee(){}
            struct FoncteurDerivee
            {
                void operator()(Derivee /*const*/ & d) /*const*/
                {
                    std::cout<<"derivee"<<std::endl;
                }
            };
    };
    int main()
    {
        std::vector<Base> tabBase;
        for(int i=0;i<10;++i)
            tabBase.push_back(Base());
        std::vector<Derivee> tabDerivee;
        for(int i=0;i<10;++i)
            tabDerivee.push_back(Derivee());
        for_each(tabBase.begin(),tabBase.end(),Base::FoncteurBase());
        /* polymorphisme inside ? */
        for_each(tabDerivee.begin(),tabDerivee.end(),Base::FoncteurBase());
        /* pas de polymor ici */
        for_each(tabDerivee.begin(),tabDerivee.end(),Derivee::FoncteurDerivee());
        return 0;
    }
    Évidemment, tu perds l'avantage des template si le comportement reste identique, mais tu gagne le polymorphisme éventuel...

    Et je ne parle même pas de la possibilité de créer un foncteur imbriqué template

    Enfin, bref, comme tu peux le remarquer, les foncteurs te permettent à mon sens clairement une plus grande souplesse...
    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 très actif Avatar de metagoto
    Profil pro
    Hobbyist programmateur
    Inscrit en
    Juin 2009
    Messages
    646
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Hobbyist programmateur

    Informations forums :
    Inscription : Juin 2009
    Messages : 646
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Effectivement, la fonction pourrait se présenter sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    template<class T, class iter=std::iterator_traits<T> >
    void foo(iter it, iter end)
    {
        while(it!=end)
        {
            (*it).laFonctionQuiVaBien();
            ++it;
        }
    }
    En fait il n'y a pas besoins de déclarer un paramètre T puis un paramètre Iter ensuite avec une default value. Directement template<typename Iter> suffit.
    Aussi, normalement les functions template n'acceptent pas de paramètres templates par défaut.

    mais, le jour où tu voudrais faire en sorte qu'elle fonctionne avec une std::map (ou autre container associatif utilisant des tuple), il faudra créer une fonction dont le nom diffère et qui serait proche de
    C'est intéressant parce que j'ai pas souvenir d'avoir vu un "type traits" tout fait pour pouvoir opérer cette distinction entre les différentes manières d'accéder au type déréférencé via un itérateur pour les containers standards. (j'ai regardé boost type traits, mais rien). Du coup j'ai testé un truc, que voilà:

    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
    // lib part
    struct true_ { };
    struct false_ { };
     
    template<typename T>
    struct is_pair
    {
      typedef false_ type;
    };
     
    template<typename K, typename V>
    struct is_pair<std::pair<K,V> >
    {
      typedef true_ type;
    };
     
     
    // specialisée pour type assoc
    template <class Iter>
    void doSomethingImpl(Iter first, Iter last, const true_&) {
      // deref avec it->second
    }
     
    // specialisée pour type normal
    template <class Iter>
    void doSomethingImpl(Iter first, Iter last, const false_&) {
      // deref avec *it
    }
     
    // interface
    template <class Iter>
    void doSomething(Iter first, Iter last) {
      typedef typename std::iterator_traits<Iter>::value_type value_type;
      doSomethingImpl(first, last, typename is_pair<value_type>::type());
    }
     
    doSomething(map.begin(), map.end());
    doSomething(vec.begin(), vec.end());
    [*]il est préférable d'utiliser les fonctions qui manipulent des écart [debut,fin) plutôt que des boucle
    Ca revient plus ou moins au même. Cependant, quand on est certain de boucler sur tous les éléments sur tout un range, j'ai plus l'habitude d'utiliser un for qu'un while. Je ne sais pas pourquoi, une question de feeling sûrement

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

Discussions similaires

  1. Réponses: 5
    Dernier message: 26/07/2007, 19h09
  2. Vector , passage de double entre vector
    Par croumir dans le forum SL & STL
    Réponses: 3
    Dernier message: 14/06/2007, 22h39
  3. Réponses: 7
    Dernier message: 22/10/2006, 18h51
  4. [VB6] [MDI] Signaler la fermeture d'une fille à la mère
    Par cpri1shoot dans le forum VB 6 et antérieur
    Réponses: 4
    Dernier message: 13/04/2004, 08h57
  5. Réponses: 2
    Dernier message: 11/07/2003, 18h24

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