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 :

Appeler le bon constructeur par déplacement à partir de la classe de base


Sujet :

C++

  1. #1
    Membre émérite Avatar de Steph_ng8
    Homme Profil pro
    Doctorant en Informatique
    Inscrit en
    Septembre 2010
    Messages
    677
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Doctorant en Informatique

    Informations forums :
    Inscription : Septembre 2010
    Messages : 677
    Par défaut Appeler le bon constructeur par déplacement à partir de la classe de base
    Bonjour.
    Je dois stocker dans un conteneur des objets polymorphiques.
    Ce qui m'oblige à utiliser des pointeurs.

    Les classes en question n'ont pas de constructeur par défaut et ne sont ni copiables ni assignables.
    Mais elles sont déplaçables et swappables.

    Vu que le conteneurs est censé stocker les objets, il est responsable de leur création et de leur destruction.
    J'utilise donc std::unique_ptr.

    Le problème, c'est d'arriver à créer les bonnes instances d'objet.

    La première idée qui m'est venue fut de faire une fonction clone dont le but est créer une copie par déplacement, allouée dynamiquement, à redéfinir dans les classes filles.
    (Je sais, le nom n'est pas très bien choisi...)
    Sauf qu'au final, toutes les redéfinitions auront la même tête, il y aura juste de nom de la classe (après new) qui changera.
    Ça fait duplication de code...

    Bon, voyons ce que les templates peuvent m'apporter...
    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
    class A
    {
     
      private:
        template <class T>
        static
        std::unique_ptr<A> clone(T&& x)
        {
            return std::unique_ptr( new T(std::move(x)) );
        }
     
      public:
        std::unique_ptr<A> clone()
        {
            return clone(std::move(*this));
        }
     
    };
    Je me disais que la fonction membre statique serait capable de déterminer le type réel de l'objet x.
    Sauf que vu que je l'appelle depuis un A, elle ne peut pas...


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    class A
    {
     
      public:
        template <class T>
        static
        std::unique_ptr<A> clone(A&& x)
        {
            typedef typename std::remove_reference<T>::type type;
     
            return std::unique_ptr( new type(std::move(x)) );
        }
     
    };
    Commet cela, ça fonctionne déjà mieux.
    J'ai juste oublié que lors de l'insertion :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class container : private std::vector< std::unique_ptr<A> >
    {
     
      private:
        typedef std::vector< std::unique_ptr<A> > _base;
     
      public:
        void insert(A&& x)
        {
            _base::push_back( A::clone(std::move(x)) );
        }
    };
    je passe encore via un A.


    L'autre solution que je vois, c'est de rendre la fonction insert template, et d'y créer la copie.
    Dans ce cas, on pourra se rendre compte à la compilation si l'on essaie d'insérer un objet qui n'hérite pas de A ?

    Si vous avez d'autres suggestions, je suis preneur...

  2. #2
    Expert confirmé
    Avatar de Luc Hermitte
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2003
    Messages
    5 287
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Août 2003
    Messages : 5 287
    Par défaut
    ???
    vec.push_back(new B());

    Pas besoin de faire appel à move. Tel qu'il est fait le vecteur est là pour stocker des pointeurs vers des objets dynamiques, et pas des instances que tu déplaces vers des pointeurs.

    Je pense que tu t'embêtes énormément à manipuler des données de deux niveaux différents de deux façons qui n'ont rien à voir.
    Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
    Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...

  3. #3
    Membre Expert

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Par défaut
    A mon humble avis, vu que le problème est similaire à boost.ptr_container en C++03, il est probablement plus simple d'adopter la même solution que boost.ptr_container, à savoir :

    Ne pas tenter de cacher que le conteneur manipule des pointeurs et adapter l'interface en conséquence.

    Par exemple, un push_back classique de std::vector std::vector<T>::push_back(const T& v);
    devient avec boost::ptr_vector boost::ptr_vector<T>::push_back(std::auto_ptr<int> v)
    Donc en résumé, remplacer
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    void insert(A&& x)
    {
         _base::push_back( A::clone(std::move(x)) );
    }
    par
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    void insert(std::unique_ptr<A> x)
    {
         _base::push_back(std::move(x));
    }
    élimine tous ces problèmes difficiles de clone() et j'ai aussi tendance à pense que c'est de toute façon plus clair pour l'utilisateur (car est-ce que l'on souhaite vraiment planquer le fait qu'un objet polymorphique doit être sur le tas ?)

    La première idée qui m'est venue fut de faire une fonction clone dont le but est créer une copie par déplacement, allouée dynamiquement, à redéfinir dans les classes filles.
    (Je sais, le nom n'est pas très bien choisi...)
    Sauf qu'au final, toutes les redéfinitions auront la même tête, il y aura juste de nom de la classe (après new) qui changera.
    Ça fait duplication de code...
    Il me semble que ce n'est pas possible de factoriser un tel code. Comme expliqué ici le C++ ne dispose pas de "constructeur virtuel", donc est on bien forcé de palier avec notre propre fonction clone() et il faut alors l'écrire à la main dans toutes les classes filles. (Bon, maintenant, je ne maitrise pas vraiment cette partie du C++, je suppose qu'il faudrait se plonger dans l'immense article-somme de 3Darchi pour y voir plus clair ) D'ailleurs, je remarque que boost.ptr_container propose lui aussi un mécanisme pour cette notion de clonage, un peu différent car à base d'une fonction template new_clone qui sert de factory, mais là encore il faut surcharger cette fonction new_clone pour tous nos types et classes filles donc ça revient au même.

    Dans ce cas, on pourra se rendre compte à la compilation si l'on essaie d'insérer un objet qui n'hérite pas de A ?
    C'est peut être possible en C++11 avec static_insert + le trait std::is_base_of ?

  4. #4
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Par défaut
    Je ne suis pas certain d'avoir compris le problème, mais as-tu vu que vector fournit une fonction enplace_back qui te permet de construire directement l'élément dans le vecteur ?
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  5. #5
    Membre émérite Avatar de Steph_ng8
    Homme Profil pro
    Doctorant en Informatique
    Inscrit en
    Septembre 2010
    Messages
    677
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Doctorant en Informatique

    Informations forums :
    Inscription : Septembre 2010
    Messages : 677
    Par défaut
    À l'origine, je n'étais parti sur l'idée du « clone » pour masquer l'utilisation des pointeurs, mais pour être sûr que la durée de vie des objets stockés n'excède pas celle du conteneur, et aussi pour pouvoir passer des temporaires aux fonctions d'insertions.
    (D'ailleurs, c'est celui qui crée qui détruit, non ?)
    Sûrement aussi pour d'autres raisons, mais j'ai oublié...
    En y réfléchissant, ce n'était peu être pas une bonne idée de faire comme ça ; c'est vrai que je me complique la vie.

    Citation Envoyé par Arzar Voir le message
    Il me semble que ce n'est pas possible de factoriser un tel code.
    C'est bien ce que je me disais...

    Citation Envoyé par Arzar Voir le message
    C'est peut être possible en C++11 avec static_insert + le trait std::is_base_of ?
    Après coup, j'avais essayé.
    Et ça fonctionne.
    Mais ce n'est même pas nécessaire : le code ne compile pas si on essaie d'ajouter un objet qui n'est pas un A (directement ou indirectement).
    Ceci dit, avec static_assert, on peut mettre un message d'erreur clair.

    Citation Envoyé par JolyLoic Voir le message
    Je ne suis pas certain d'avoir compris le problème, mais as-tu vu que vector fournit une fonction enplace_back qui te permet de construire directement l'élément dans le vecteur ?
    Oui, j'ai vu.
    (Je me disais bien que j'avais oublié quelque chose dans mon premier message... )
    L'inconvénient, c'est qu'il faudrait renseigner à chaque appel le type réel de l'objet.
    Mais bon, avec un temporaire, au final c'est pareil...

    Alors je vais me tourner vers une adaptation de cette dernière possibilité.
    Il y aura un paramètre template qui devra être donné explicitement, mais bon tant pis.
    C'est un moindre mal.

    Merci de vos réponses.

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

Discussions similaires

  1. Réponses: 5
    Dernier message: 13/05/2009, 18h22
  2. Réponses: 9
    Dernier message: 03/08/2008, 14h45
  3. Réponses: 21
    Dernier message: 05/04/2008, 20h30
  4. probleme appel constructeur par default en extern
    Par Delgador dans le forum C++
    Réponses: 3
    Dernier message: 25/04/2007, 16h15
  5. Appel d'un constructeur à partir d'un autre
    Par Pragmateek dans le forum Langage
    Réponses: 28
    Dernier message: 18/06/2006, 01h07

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