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 :

Prototype du constructeur d'une classe template


Sujet :

Langage C++

  1. #1
    Membre averti
    Homme Profil pro
    Enseignant Chercheur
    Inscrit en
    Juillet 2009
    Messages
    122
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Enseignant Chercheur

    Informations forums :
    Inscription : Juillet 2009
    Messages : 122
    Points : 306
    Points
    306
    Par défaut Prototype du constructeur d'une classe template
    Bonjour à tous,

    je bosse actuellement sur une petite bibliothèque personnelle et j'ai eu besoin d'écrire une classe pour gérer des sortes de vecteurs. Pour certaines raisons, la taille de ces vecteurs ne doit pas être modifiable par l'utilisateur de la bibliothèque. Pour ne pas avoir à re-écrire le même code pour les différentes tailles de vecteurs, j'ai utilisé une classe template du style suivant:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    template <unsigned int N>
    class V
    {
      public:
        V() {}
      private:
        float m_data[N];
    };
    }
    Je voudrais maintenant que l'utilisateur puisse passer directement les valeurs voulues au vecteur créé. Quelque chose comme
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    V<3> v(1.0f, 2.0f, 3.0f);
    Cependant, cela pré-suppose de connaître la taille du vecteur pour déterminer le nombre d'arguments du constructeur. De plus, je ne veux pas que le constructeur à 3 arguments puisse être utilisé pour créer des V<2> ou des V<4>. La solution que j'ai trouvée est de déclarer les prototypes de ces constructeurs mais de n'implémenter que les versions spécialisées.
    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
    template <unsigned int N>
    class V
    {
      public:
        V() {}
        V(float x, float y, float z); // Constructeur pour V<3>
      private:
        float m_data[N];
    };
     
    // Pas d'implémentation de V<N>::V(float, float, float) en général
    // Uniquement celle spécialisée pour V<3>
    template <>
    V<3>::V(float x, float y, float z)
    {
      m_data[0] = x;
      m_data[1] = y;
      m_data[2] = z;
    }
    Cette solution me va pour l'instant mais ne me semble pas très élégante. De plus, l'inconvénient pour l'utilisateur est que si il écrit
    l'erreur n'apparaît qu'à l'édition des liens et pas à la compilation... Cela bloque bien l'utilisation d'un prototype inadéquate mais ne retourne pas le fichier et la ligne de l'erreur (pas pratique pour le debug).
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    $ g++ -c main.cpp
    $ g++ main.o
    main.cpp:(.text+0x6b): undefined reference to `V<2u>::V(float, float, float)'
    collect2: ld returned 1 exit status
    Y a-t-il une meilleure solution pour créer des constructeurs spécialisés selon la valeur d'un paramètre passé en template et qui ne soient utilisables que pour certaines valeurs?

    Merci à tous

  2. #2
    Membre averti
    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2007
    Messages
    634
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2007
    Messages : 634
    Points : 407
    Points
    407
    Par défaut
    Salut,

    Si j'ai bien compris ton problème, tu souhaites passer un nombre variable de paramètres au constructeur de ta classe et ce en fonction de ton N.

    Personnellement, je passerais plutôt une liste, où un vector... (conteneur de la std).


    Après si tu tiens à ce que ton constructeur prenne le nombre d'argument voulu, tu peux utiliser les va_arg. Cela te permet de "dépiler" autant d'argument que tu souhaite à ta fonction.

    Ainsi,
    si N vaut 2, tu dépilera 2 arguments...
    si N vaut 3, tu dépilera 3 arguments... etc


    Pour plus d'infos sur les va_args : Ici

    NeoKript

  3. #3
    Membre averti
    Homme Profil pro
    Enseignant Chercheur
    Inscrit en
    Juillet 2009
    Messages
    122
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Enseignant Chercheur

    Informations forums :
    Inscription : Juillet 2009
    Messages : 122
    Points : 306
    Points
    306
    Par défaut
    Merci pour ta réponse.

    Citation Envoyé par NeoKript Voir le message
    Personnellement, je passerais plutôt une liste, où un vector... (conteneur de la std).
    La taille de mes sortes de vecteurs ne devrait pas dépasser 5. Je trouve donc pratique de permettre à l'utilisateur de passer directement les valeurs en argument. Après, je suis d'accord avec toi, un std::vector peut faire l'affaire (j'ai d'ailleurs déjà prévu un constructeur en ce sens).

    Citation Envoyé par NeoKript Voir le message
    Après si tu tiens à ce que ton constructeur prenne le nombre d'argument voulu, tu peux utiliser les va_arg. Cela te permet de "dépiler" autant d'argument que tu souhaite à ta fonction.
    J'avais pensé à cela mais ça ne résout pas mon problème. Si le prototype d'un constructeur est
    il sera toujours possible de construire un V<2> avec 3 arguments (ce cas n'est pas très gênant car seuls les deux premiers arguments seront utilisés) ou un V<3> avec 2 arguments seulement (celui-ci pose problème car le programme va aller chercher la dernière valeur dans la pile... donc résultat imprévisible car il y a un argument manquant). Impossible de vérifier cela à la compilation...

  4. #4
    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 612
    Points
    30 612
    Par défaut
    Salut,

    Pourquoi ne prévoirais tu pas un constructeur prenant une intervalle entre la première valeur à insérer et ce qui suit la dernière valeur

    Tu pourrais, en outre, profiter du fait que tu as déterminé la taille de ton vecteur pour vérifier que l'utilisateur fournit bien le nombre requis de données

    Un petit truc vite fait:
    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
    template <unsigned int N>
    class MyVector
    {
        public:
            enum {size = N };
            // permet l'utilisation de n'importe quel itérateur compatible
            template <typename iterator>
            MyVector(iterator begin, iterator end)
            {
                unsigned int count =0;
                while(begin != end && count!= N)
                {
                    data[count]=(*begin);
                    ++begin;
                    ++count;
                }
                assert(count == N);
                assert(begin == end);
            }
        private:
            double data[N];
    };
    Tu pourrais alors l'utiliser sous une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    int main()
    {
        double tab[]={2.0, 3.0, 3.1415926, 22.6, 46.8741};
        MyVector<2> vect(tab, tab+2);
        MyVector<3> vect2(& tab[1]; & tab[1]+2);
     
    }
    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 averti
    Homme Profil pro
    Enseignant Chercheur
    Inscrit en
    Juillet 2009
    Messages
    122
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Enseignant Chercheur

    Informations forums :
    Inscription : Juillet 2009
    Messages : 122
    Points : 306
    Points
    306
    Par défaut
    La méthode que tu proposes est jolie: à la façon des std::vector et en gérant la taille en plus. Merci.

    Cependant, elle ne permet pas de passer les valeurs à la volée comme le ferait un "bête" constructeur à N arguments. On ne gagne donc pas grand chose par rapport à un constructeur basé sur un std::vector.

    Je reste donc pour l'instant sur ma première idée qui bloque l'utilisation d'un constructeur mal adapté au moment de l'édition des liens. De plus, pour retrouver le fichier et la ligne qui poseraient problème, l'utilisateur peut toujours compiler avec les informations de debug...

    Sinon, j'ai aussi pensé à faire une sorte de typedef enrichie grâce à l'héritage (avec m_data en protected dans la classe template):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    class V3 : public V<3>
    {
      public:
        V3(float x, float, y, float z);
    }
    Cela ne m'oblige qu'à re-écrire, pour chaque taille voulue, les parties non-héritées de la classe (operateur d'affectation, ...). Est-ce que cela vous semble une bonne idée ou bien est-ce que je vais dans le mur? En effet, je crains un peu les problèmes à venir entre V<3> et V3 puisqu'il ne s'agit pas que d'un simple typedef.

  6. #6
    Membre chevronné
    Avatar de Goten
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 580
    Détails du profil
    Informations personnelles :
    Âge : 33
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 580
    Points : 2 205
    Points
    2 205
    Par défaut
    Tu peux te permettre de compiler en 1x?
    "Hardcoded types are to generic code what magic constants are to regular code." --A. Alexandrescu

  7. #7
    Membre averti
    Homme Profil pro
    Enseignant Chercheur
    Inscrit en
    Juillet 2009
    Messages
    122
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Enseignant Chercheur

    Informations forums :
    Inscription : Juillet 2009
    Messages : 122
    Points : 306
    Points
    306
    Par défaut
    Citation Envoyé par Goten Voir le message
    Tu peux te permettre de compiler en 1x?
    Je ne comprends pas ta question, que veux-tu dire par "compiler en une fois"?

  8. #8
    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 : 49
    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
    Points : 16 213
    Points
    16 213
    Par défaut
    Si ta classe est un aggragat, tu peux en initialiser directement les éléments sans passer par le constructeur. Regarde que si est fait dans boost::array, par exemple.

    Autrement, j'imagine que des solutions à base de bidouille de template metaprogramming doivent pouvoir faire des choses sur un compilateur où les variadic templates sont supportés, ceci afin d'avoir une erreur de compilation plutôt qu'une erreur au link en cas de problème (les erreurs à la compilation sont souvent plus claires, en particulier par rapport au point dans le code où est le problème).
    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.

  9. #9
    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 612
    Points
    30 612
    Par défaut
    Citation Envoyé par Meseira Voir le message
    Je ne comprends pas ta question, que veux-tu dire par "compiler en une fois"?
    LOOLLL...

    Il demandait, en fait, si tu pouvais te permettre de compiler en (C++)1x : ne nom de code de la prochaine norme en cours de finalisation, mais pour laquelle on n'a pas encore de date définitive de mise à disposition
    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

  10. #10
    Membre averti
    Homme Profil pro
    Enseignant Chercheur
    Inscrit en
    Juillet 2009
    Messages
    122
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Enseignant Chercheur

    Informations forums :
    Inscription : Juillet 2009
    Messages : 122
    Points : 306
    Points
    306
    Par défaut
    Citation Envoyé par koala01 Voir le message
    LOOLLL...

    Il demandait, en fait, si tu pouvais te permettre de compiler en (C++)1x


    Il faut que j'arrête de me méfier du langage SMS...

    @Goten oui, je peux compiler en 1x. As-tu une solution dans ce cas?

    @JolyLoic j'ai jeté un coup d'oeil rapide sur les notions dont tu parles. Cela a l'air intéressant... faut voir (je regarderai tout ça à tête reposée ce soir).
    Citation Envoyé par JolyLoic Voir le message
    les erreurs à la compilation sont souvent plus claires, en particulier par rapport au point dans le code où est le problème
    Tout à fait d'accord avec toi... c'est même cette remarque qui m'a fait venir poster mon problème vu que la seule solution que j'avais trouvée donnait des erreurs au link mais rien à la compilation.

  11. #11
    Invité
    Invité(e)
    Par défaut
    Dans ce cas tu as deux solutions : les Variadics templates(en bon français) et les liste d'initialisateurs(pas confondre avec celles-z-en -tion) :
    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
     
    //Variadics
    template<int N>
    class V
    {
    public:
         template<class Args...>
         V(Args... args)
         {
               static_assert(sizeof...(Args) == N);
               init(args...);
         }
    private:
         template<class T, class Args...>
         void init(T t,Args... args)
         {
                mData[N-sizeof...(Args)] = t;
                if(N != sizeof...(Args))
                {
                     init(args...);
                }
         } 
    };
     
    // Liste d'initialisation
    template<int N>
    class V
    {
    public:
        V(std::initializer_list<float> const& init)
        {
             assert(init.size() == N); // attention, pas statiquement typé
             std::copy(init.begin(), init.end(), mData);
        }
    };

  12. #12
    Membre averti
    Homme Profil pro
    Enseignant Chercheur
    Inscrit en
    Juillet 2009
    Messages
    122
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Enseignant Chercheur

    Informations forums :
    Inscription : Juillet 2009
    Messages : 122
    Points : 306
    Points
    306
    Par défaut
    Merci Joe Dralliam, la solution avec les variadic templates est impec'! Quelques corrections pour que le code compile:
    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
    template <unsigned int N>
    class V
    {
      public:
        template <class... Args>
        V(Args... args)
        {
          static_assert(sizeof...(Args) == N, "Taille incorrecte");
          init(args...);
        }
     
      private:
        void init() {} // Pour conclure la récurrence et pour V<0>
     
        template <class T, class... Args>
        void init(T t, Args... args)
        {
          m_data[N - sizeof...(Args) - 1] = t;
          init(args...);
        }
     
        float m_data[N];
    };
    Merci à tous pour votre aide

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

Discussions similaires

  1. Réponses: 11
    Dernier message: 26/02/2015, 01h20
  2. Réponses: 28
    Dernier message: 12/11/2012, 15h32
  3. constructeur template dans une classe template
    Par regis.portalez dans le forum Langage
    Réponses: 16
    Dernier message: 13/12/2010, 16h50
  4. Réponses: 3
    Dernier message: 06/11/2005, 18h02
  5. Trouver le Type d'une classe template dynamiquement ?
    Par Serge Iovleff dans le forum Langage
    Réponses: 3
    Dernier message: 23/09/2005, 16h48

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