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 :

Template container en C++


Sujet :

C++

  1. #1
    Futur Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2014
    Messages
    6
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 29
    Localisation : France, Sarthe (Pays de la Loire)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2014
    Messages : 6
    Points : 7
    Points
    7
    Par défaut Template container en C++
    Bonjour,

    Je débute en C++. Je veux faire une classe PersistenceDiagram qui :

    1) contient un ensemble de points (de type Diagram_point, cf typedef) dans un container de la stl
    2) dispose d'un certain nombre de méthodes, disons une méthode pour ajouter des points + un iterator

    Seulement je veux qu'on puisse choisir quel container de la stl on utilise (list, vector, etc..) pour stocker les points dans le PersistenceDiagram à chaque fois qu'on en crée un. Donc PersistenceDiagram doit être un template, le paramètre de ce template est un container de la stl, c'est à dire un autre template. J'ai écrit ça :

    Header :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    #pragma once
    #include <utility>
    #include <list>
    typedef typename std::pair<double,double> Diagram_point;
    template < template< class > Container > class PersistenceDiagram{
    private:
        Container< Diagram_point > points;
    public:
        template < template<class> Container > PersistenceDiagram();
        void add_point(Diagram_point p);
        template < template<class> Container > typename Container< Diagram_point >::iterator begin();
        template < template<class> Container > typename Container< Diagram_point >::iterator end();
    };
    .cpp :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    #include "persistenceDiagram.hpp"
    template < template<class> Container > PersistenceDiagram::PersistenceDiagram(){
        points = Container< Diagram_point >();
    }
    void PersistenceDiagram::add_point(Diagram_point p){
        points.push_back(p);
    }
    template < template<class> Container > typename Container< Diagram_point >::iterator PersistenceDiagram::begin(){
        return points.cbegin();
    }
    template < template<class> Container > typename Container< Diagram_point >::iterator PersistenceDiagram::end(){
        return points.cend();
    }
    Exemple d'utilisation qui doit être possible :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    PersistenceDiagram<std::list> my_persistence_diagram();
    PersistenceDiagram<std::vector> my_persistence_diagram2();
    my_persistence_diagram.add_point(my_point); // my_point est de type Diagram_point
    Pour l'instant g++ me balance plusieurs pages d'erreurs, je pense qu'il est sage de se limiter à la première pour commencer :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    In file included from main.cpp:15:0:
    persistenceDiagram.hpp:10:28: error: expected 'class' before 'Container'
    template < template< class > Container > class PersistenceDiagram{
                                ^
    Je ne vois vraiment pas pourquoi il me dit ça, j'ai déjà indiqué le type du paramètre du template : il s'agit d'un autre template, ce dernier prend en paramètre une classe. Ca n'a pas de sens de me demander de mettre un deuxième type pour Container...

    Merci d'avance,
    Je me tiens à votre disposition pour toute précision/reformulation/explication nécessaire.

  2. #2
    Membre expérimenté Avatar de Trademark
    Profil pro
    Inscrit en
    Février 2009
    Messages
    762
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2009
    Messages : 762
    Points : 1 396
    Points
    1 396
    Par défaut
    Salut,

    Comme le compilateur te le dis :

    expected 'class' before 'Container'
    Donc tu mets 'class' avant 'Container' ce qui donne :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    template <template<class> class Container> ...
    Bon t'auras encore d'autres problème vu que vector prend 2 arguments template (comme l'allocator), et puis après les std::set te poseront problèmes vu qu'il y a 3 arguments template. En gros si j'étais toi je me passerais des arguments template template, c'est pas terrible et pas souvent utilisé (j'ai des solutions plus élégantes si tu tiens vraiment à utiliser cette technique). À choisir tu peux faire quelque chose comme :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    PersistantDiagram<std::vector<DiagramPoint>> diag;
    ...
    Aussi est-ce que ton persistant diagram aura des responsabilités supplémentaires par rapport à un container classique, en quoi il diffère d'un simple container ?

  3. #3
    Futur Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2014
    Messages
    6
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 29
    Localisation : France, Sarthe (Pays de la Loire)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2014
    Messages : 6
    Points : 7
    Points
    7
    Par défaut
    Avant tout, merci pour ta réponse.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    template <template<class> class Container> ...
    D'accord mais je ne comprends pas ce que vient faire le mot class ici, le type de Container est template<class>, non ?

    Bon t'auras encore d'autres problème vu que vector prend 2 arguments template (comme l'allocator), et puis après les std::set te poseront problèmes vu qu'il y a 3 arguments template. En gros si j'étais toi je me passerais des arguments template template, c'est pas terrible et pas souvent utilisé (j'ai des solutions plus élégantes si tu tiens vraiment à utiliser cette technique).

    Aussi est-ce que ton persistant diagram aura des responsabilités supplémentaires par rapport à un container classique, en quoi il diffère d'un simple container ?
    Bonnes questions qui vont m'aider à avancer, merci. Faut que j'y réfléchisse, je uperai/editerai demain.

  4. #4
    Membre expérimenté Avatar de Trademark
    Profil pro
    Inscrit en
    Février 2009
    Messages
    762
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2009
    Messages : 762
    Points : 1 396
    Points
    1 396
    Par défaut
    Citation Envoyé par NoVGAcable Voir le message
    D'accord mais je ne comprends pas ce que vient faire le mot class ici, le type de Container est template<class>, non ?
    Vu qu'un container est déclaré de cette manière :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    template <class T, class Alloc>
    class vector;
    et pas de cette manière :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    template <class T, class Alloc>
    vector;
    Il est plus logique de déclarer ton template comme tu déclares la classe, à mon avis c'est la raison sous-jacente, après pour ce qui est de la syntaxe du bouzin, je suis d'accord que ça fait un peu "bricolé", mais généralement le C++ est un peu un langage comme ça vu qu'il est plutôt mis-à-jour avec un processus d'ingénierie plutôt que de recherche.

  5. #5
    Membre expert
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    1 415
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 1 415
    Points : 3 156
    Points
    3 156
    Par défaut
    Citation Envoyé par Trademark Voir le message
    j'ai des solutions plus élégantes si tu tiens vraiment à utiliser cette technique
    Je suis très intéressé par ça, si tu pouvais développer J'ai un code que je n'ai pas réussi à écrire sans argument template template, alors tout conseil pour proprifier est bon à prendre pour moi.
    Find me on github

  6. #6
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 704
    Points
    2 704
    Par défaut
    Citation Envoyé par NoVGAcable Voir le message
    D'accord mais je ne comprends pas ce que vient faire le mot class ici, le type de Container est template<class>, non ?
    Il y a une explication dans le chapitre 5.4 de C++ Templates - The Complete Guide.

  7. #7
    Membre expérimenté Avatar de Trademark
    Profil pro
    Inscrit en
    Février 2009
    Messages
    762
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2009
    Messages : 762
    Points : 1 396
    Points
    1 396
    Par défaut
    Citation Envoyé par jblecanard Voir le message
    Je suis très intéressé par ça, si tu pouvais développer J'ai un code que je n'ai pas réussi à écrire sans argument template template, alors tout conseil pour proprifier est bon à prendre pour moi.
    Dans l'exemple de l'OP, j'aurais simplement utiliser du "rebinding de type" pour faciliter l'utilisateur, ce qui donnerait :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    namespace simple{
     
    template <class T>
    using vector = std::vector<T>;
     
    template <class T>
    using set = std::set<T>;
     
    }
     
    PersistantDiagram<simple::vector> diag;
    Ça permet d'uniformiser l'interface template des arguments. Par contre ça utilise encore les template-template. Une solution n'utilisant pas les templates-templates serait :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    struct arg1;
     
    PersistantDiagram<std::vector<arg1>> diag;
    Avec la classe PersistantDiagram utilisant un mécanisme pour "rebind" le type comme :

    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
     
    template <class NewType, class T> struct rebind_type;
     
    template <class NewType, class T, class Alloc>
    struct rebind_type<NewType, std::vector<T, Alloc>>
    {
      using type = std::vector<NewType, Alloc>;
    };
     
    template <class NewType, class T, class Alloc, class Compare>
    struct rebind_type<NewType, std::set<T, Alloc, Compare>>
    {
      using type = std::set<NewType, Alloc, Compare>;
    };
     
    template <class NewType, class T>
    using rebind_type_t = rebind_type<NewType, T>::type;
     
    // Et on utilise ça comme ça :
    template <class Container>
    class PersistantDiagram
    {
      using container_type = rebind_type_t<Diagram, Container>;
    };
    C'est donc similaire à std::bind mais pour les templates, à mon avis mpl::bind est le système généralisé mais j'ai pas cherché à l'appliquer à l'exemple pour voir si c'était exactement ça.

  8. #8
    Futur Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2014
    Messages
    6
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 29
    Localisation : France, Sarthe (Pays de la Loire)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2014
    Messages : 6
    Points : 7
    Points
    7
    Par défaut
    Merci pour ces réponses, j'ai compris pas mal de choses, je me suis inspiré de vos propositions ainsi que du chapitre 5.4 de C++ Templates - The Complete Guide pour faire ceci :

    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
     
    #pragma once
    #include <utility>
     
    typedef typename std::pair<double,double> Diagram_point;
     
    template < template<typename E, typename = std::allocator< E > > class ContainerT >
    class PersistenceDiagram{
     
    public:
      typedef ContainerT< Diagram_point > Container;
      typedef typename Container::const_iterator Diagram_point_iterator;
     
    private:
      Container points;
     
    public:
      PersistenceDiagram(){
          points = Container();
      }
     
      void add_point(Diagram_point p){
          points.push_back(p);
      }
     
      Diagram_point_iterator begin(){
          return points.cbegin();
      }
     
      Diagram_point_iterator end(){
          return points.cend();
      }
    };
    Ça marche. Il n'y a plus qu'un unique fichier vu que c'est un template. Merci à tous.

    Edit : Sauf avec les std::set forcément vu qu'ils ont besoin d'un comparator.

  9. #9
    Membre expert
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    1 415
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 1 415
    Points : 3 156
    Points
    3 156
    Par défaut
    Pas mal cette technique de rebind de type ! Voilà comment je l'utiliserais:

    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
    #include <vector>
     
    struct unused_type;
     
    template <typename New, typename Unused> struct rebind_type;
     
    template <typename New, typename Unused, typename ... Args>  struct rebind_type<New, std::vector<Unused, Args...>> {
      using type = std::vector<New, Args...>;
    };
     
    template <typename DataType, typename Container> class SelfContainer {
      using container_type = typename rebind_type<SelfContainer*, Container>::type;
      DataType value_;
      container_type children_;
     public:
      SelfContainer() {};
      SelfContainer(DataType const& value) : value_(value) {}
      void AddChild(SelfContainer* child) { children_.push_back(child); }
    };
     
    int main() {
      SelfContainer<int, std::vector<unused_type> > c1;
      SelfContainer<int, std::vector<unused_type> > c2;
      c1.AddChild(&c2);
      return 0;
    }
    Le problème était de pouvoir créer une classe template pouvant agréger des pointeurs vers des instances de la même classe. Je n'avais pas réussi sans template template, c'est maintenant chose faite.
    Find me on github

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

Discussions similaires

  1. Bonne pratique des templates - Container
    Par LittleWhite dans le forum C++
    Réponses: 5
    Dernier message: 04/09/2012, 18h55
  2. Différenciation array <> container dans un template
    Par Trademark dans le forum Langage
    Réponses: 8
    Dernier message: 17/08/2011, 02h39
  3. Template, Container Sequence, Vector et Deque
    Par Ange44 dans le forum Langage
    Réponses: 5
    Dernier message: 30/07/2010, 18h30
  4. fonction template pour container
    Par Bourrine dans le forum Langage
    Réponses: 8
    Dernier message: 26/06/2007, 14h55
  5. [XSLT] template
    Par demo dans le forum XSL/XSLT/XPATH
    Réponses: 4
    Dernier message: 09/09/2002, 12h31

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