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 :

Spécialisation de membre d'une classe template


Sujet :

Langage C++

  1. #1
    Membre actif Avatar de Rewpparo
    Homme Profil pro
    Amateur
    Inscrit en
    Décembre 2005
    Messages
    170
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Amateur

    Informations forums :
    Inscription : Décembre 2005
    Messages : 170
    Points : 281
    Points
    281
    Par défaut Spécialisation de membre d'une classe template
    Bonjour,
    Je suis en train de redesigner une partie de mon code. Pour l'instant j'ai :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    template <typename REAL, unsigned D> class Vector
    {
       REAL m_data[D];
    };
     
    ///Cross product
    template<typename REAL> Vector<REAL,3> operator ^(const Vector<REAL,3>& p_v1, const Vector<REAL,3>& p_v2);
    Le cross product n'est défini que pour les vecteurs en 3D. Ce que j'aimerais faire, c'est déplacer quand même l'opérateur dans la classe, de cette facon :
    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
    template <typename REAL, unsigned D> class Vector
    {
       REAL m_data[D];
    public:
       Vector<REAL,D> operator ^(const Vector<REAL,D>& p_vect);
    };
     
    ///Cross product
    template<typename REAL> Vector<REAL,3> Vector<REAL,3>::operator ^(const Vector<REAL,3>& p_vect)
    {
       ...
    }
     
    ///Voir même éventuellement si besoin
    template<typename REAL> Vector<REAL,2> Vector<REAL,2>::operator ^(const Vector<REAL,2>& p_vect)
    {
       ...
    }
    Le but est de faire un genre de spécialisation de template, et d'avoir une erreur de linkage si quelqu'un essaie de faire un crossproduct sur des Vectors a autre chose que 3D.
    L'erreur que j'ai c'est "Invalid use of incomplete type Vector<float, 3u>" (g++). Je ne comprend pas vraiment le message, la définition de la classe est extérieure à la classe, il devrait normalement avoir ce qu'il lui faut pour instancier le template.
    Est ce possible de faire une spécialisation partielle d'un membre d'une classe ? Si oui comment, et sinon pourquoi pas ?

  2. #2
    Nouveau membre du Club
    Inscrit en
    Août 2003
    Messages
    17
    Détails du profil
    Informations forums :
    Inscription : Août 2003
    Messages : 17
    Points : 30
    Points
    30
    Par défaut
    Salut,

    Avant de pouvoir faire une spécialization de template tu as besoin d'avoir une definition générale.
    Si tu veux que ça ne compile qu'avec 3D il suffit de faire comme 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
     
    //la définition générale qui ne compilera pas
    template<typename REAL, unsigned D> Vector<REAL,D> Vector<REAL,D>::operator ^(const Vector<REAL,D>& p_vect)
    {
    // cette ligne ne sera jamais compilé a part si tu utilise autre chose qu'un 
    // vector<3>
     erreur, cross product fonctionne seulement avec 3D vector
    }
     
    // la def de Cross product qui compile correctement
    template<typename REAL> Vector<REAL,3> Vector<REAL,3>::operator ^(const Vector<REAL,3>& p_vect)
    {
       // ton code ici
    }

  3. #3
    Membre actif Avatar de Rewpparo
    Homme Profil pro
    Amateur
    Inscrit en
    Décembre 2005
    Messages
    170
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Amateur

    Informations forums :
    Inscription : Décembre 2005
    Messages : 170
    Points : 281
    Points
    281
    Par défaut
    Exactement ce que je cherchais !

    Existe t il un moyen portable d'afficher une erreur de compilation s'il essaie de compiler la version générique ?


    EDIT : Hé bien en fait non, ca ne marche pas. La définition générale est compilée, et il me donne toujours mon erreur "invalid use of incomplete type ‘class Vector<REAL, 3u>’"

    EDIT 2 : J'ai essayé de faire de mon opérateur une méthode templatée pour essayer de spécialiser le template à ce niveau. Mais en plus de ne pas marcher, je me rend compte que ca ne résout pas mon problème qui est de simplifier une instanciation implicite de la classe : je suis toujours obligé de faire une instanciation explicite pour l'opérateur.
    Je pense que j'ai surtout besoin de comprendre l'erreur 'invalid use of incomplete type' pour pouvoir avancer.

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

    Informations professionnelles :
    Activité : aucun

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

    Avec une classe (partiellement) spécialisée, il est possible d'obtenir une erreur *relativement* parlante....

    Et il y a une phrase qui dit (traduction perso ) que tout problème peut etre résolu en ajoutant un niveau d'indirection supplémentaire

    En fonction de ce que tu peux faire, tu peux très bien générer des traits de politiques pour certains type de base, par exemple, avec 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
    /* un type tout à fait générique
     */
    template <typename T>
    struct MyTrait;
    /* pour chaque type que tu envisages d'utiliser réellement
     */
    template <>
    struct MyTrait<double>
    {
        typedef double value_type;
        typedef double * ptr_type;
        typedef double cref_type;
        typedef double & ref_type;
    };
    template <typename REAL, unsinged int D>
    class Vector
    {
        typedef MyTrait<REAL> trait_type;
        public:
            typedef trait_type::value_type value_type;
        /* ...*/
        private:
            value_type  m_data[D];
    }
    tu obtiendra une erreur (assez cryptique, à moins d'utiliser les possibilités de C++11, il est vrai ) si tu essaye de créer une instance de la classe Vector avec autre chose qu'un double, car MyTrait::value_type n'existe que... pour MyTrait<double>

  5. #5
    Nouveau membre du Club
    Inscrit en
    Août 2003
    Messages
    17
    Détails du profil
    Informations forums :
    Inscription : Août 2003
    Messages : 17
    Points : 30
    Points
    30
    Par défaut
    Sinon après pour avoir une erreur de compilation un peu plus explicite tu peux toujours utiliser boost concept check: http://www.boost.org/doc/libs/1_49_0...cept_check.htm mais bon si tu n'utilise pas déjà boost je penses pas que ce soit une bonne idée de l'integrer seulement pour ça.

    Pour en revenir a ton erreur, pourrais tu montrer le code ou tu utilise ta classe template (quelque part avec un vector<float>)?

  6. #6
    Membre actif Avatar de Rewpparo
    Homme Profil pro
    Amateur
    Inscrit en
    Décembre 2005
    Messages
    170
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Amateur

    Informations forums :
    Inscription : Décembre 2005
    Messages : 170
    Points : 281
    Points
    281
    Par défaut
    Avant de m'occuper de l'erreur de compilation, il faut que j'arrive à le faire marcher pour D=3.
    Spécialiser un member non template d'une classe template, c'est possible ? Quel est le sens de mon "invalid use of incomplete type Vector<REAL,3u>" ?

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 629
    Points : 30 692
    Points
    30 692
    Par défaut
    Citation Envoyé par Rewpparo Voir le message
    Avant de m'occuper de l'erreur de compilation, il faut que j'arrive à le faire marcher pour D=3.
    As tu essayé de la manière que j'ai indiquée?
    Spécialiser un member non template d'une classe template, c'est possible ?
    tu ne peux spécialiser qu'une classe ou une structure (ou surcharger éventuellement une fonction)...

    si un membre non dépendant des types template est créé, tu ne peux le redéfinir que dans une des spécialisation de ta classe, mais, généralement, ces membres ont une raison d'être du type choisi
    Quel est le sens de mon "invalid use of incomplete type Vector<REAL,3u>" ?
    Cela signifie que le compilateur a trouvé une déclaration de la classe Vector qui fonctionne avec les arguments indiqués, mais qu'il n'a pas trouvé la définition de cette classe.

    Autrement dit, il sait que ca existe, mais il ne sait pas à quoi ca ressemble (et encore moins la taille que cela peut représenter)... Il est donc incapable de générer le code binaire correspondant

  8. #8
    Membre actif Avatar de Rewpparo
    Homme Profil pro
    Amateur
    Inscrit en
    Décembre 2005
    Messages
    170
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Amateur

    Informations forums :
    Inscription : Décembre 2005
    Messages : 170
    Points : 281
    Points
    281
    Par défaut
    Citation Envoyé par koala01 Voir le message
    As tu essayé de la manière que j'ai indiquée?
    bah les traits, c'est typiquement pour faire la spécialisation sur REAL, moi j'aimerais la faire sur D (la taille de l'array). De plus, je veux une interface publique la plus simple possible, je préfère garder l'opérateur hors de la classe plutôt que de partir dans des traits.
    Je cherchais plutôt une erreur de syntaxe ou un truc du style.

    Citation Envoyé par koala01 Voir le message
    Cela signifie que le compilateur a trouvé une déclaration de la classe Vector qui fonctionne avec les arguments indiqués, mais qu'il n'a pas trouvé la définition de cette classe.
    Oui, c'est typiquement le message qu'il sort si tu utilise une variable dans la déclaration de cette même classe, ou quand tu utilises une classe alors que tout ce qu'il a c'est une forward déclaration.
    Mais là en l'occurrence, il a toute la classe déclarée juste au dessus, et même si j'utilise que des références, ca me le fait quand même.

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 629
    Points : 30 692
    Points
    30 692
    Par défaut
    Citation Envoyé par Rewpparo Voir le message
    bah les traits, c'est typiquement pour faire la spécialisation sur REAL, moi j'aimerais la faire sur D (la taille de l'array). De plus, je veux une interface publique la plus simple possible, je préfère garder l'opérateur hors de la classe plutôt que de partir dans des traits.
    Je cherchais plutôt une erreur de syntaxe ou un truc du style.
    Ce qu'il y a bien avec les traits, c'est que tu peux faire ce que tu veux

    Un simple exemple :
    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
    template<unsigned int>
    struct DimentionTrait;
    /* pour toutes les tailles que tu acceptes */
    template <unsigned int >
    struct DimensionTrait<3>
    {
        enum {value = 3};
    };
    template <typename REAL, unsigned int D>
    class Vector
    {
        typedef DimentionTrait<D> d_trait;
        public:
            enum {dimentionMax = d_trait::value};
            typedef REAL value_type;
        private:
            value_type m_data[dimentionMax];
    };
    Oui, c'est typiquement le message qu'il sort si tu utilise une variable dans la déclaration de cette même classe, ou quand tu utilises une classe alors que tout ce qu'il a c'est une forward déclaration.
    Mais là en l'occurrence, il a toute la classe déclarée juste au dessus, et même si j'utilise que des références, ca me le fait quand même.
    Es tu sur qu'il existe une spécialisation (partielle ou total) pour le genre de classe que tu essaye de définir

    Peut etre as tu un (ou plusieurs ) message(s) du type de "required by XXX.h line YYY" qui t'indiqueront le contexte dans lequel tu essayes de déclarer ta variable, et que tu te rendras compte qu'il n'existe pas de spécialisation ad-hoc

    Il se peut aussi que tu aies simplement oublié le ; après l'accolade fermante de la définition de la classe, ca a aussi parfois ce genre d'effet

  10. #10
    Membre actif Avatar de Rewpparo
    Homme Profil pro
    Amateur
    Inscrit en
    Décembre 2005
    Messages
    170
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Amateur

    Informations forums :
    Inscription : Décembre 2005
    Messages : 170
    Points : 281
    Points
    281
    Par défaut
    Honnetement Koala, j'ai l'impression d'être encore au dessus de mon niveau avec du code comme ca. Je comprend même pas ce que c'est sensé faire.

    Sinon j'ai trouvé une solution satisfaisante pour l'instant : spécialisation totale du template car apparemment ca passe.
    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
    template <typename REAL, unsigned D> class Vector
    {
       REAL m_data[D];
    public:
       Vector<REAL,D> operator ^(const Vector<REAL,D>& p_vector);
    };
     
    template <typename REAL> Vector<REAL,3> crossProduct(const Vector<REAL,3>& p_v1, const Vector<REAL,3>& p_v2)
    {
       ///Faire le taf
    }
     
    template<> Vector<float,3> Vector<float,3>::opertator ^(const Vector<float,3>& p_vector)
    {
       return crossProduct(*this, p_vector);
    }
     
    template<> Vector<double,3> Vector<double,3>::opertator ^(const Vector<double,3>& p_vector)
    {
       return crossProduct(*this, p_vector);
    }

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 629
    Points : 30 692
    Points
    30 692
    Par défaut
    Citation Envoyé par Rewpparo Voir le message
    Honnetement Koala, j'ai l'impression d'être encore au dessus de mon niveau avec du code comme ca. Je comprend même pas ce que c'est sensé faire.
    Tu n'es pas sans savoir qu'une valeur énumérée est une expression constante (en fonction de sa position dans l'énumération ou de la valeur qu'on lui donne explicitement, voir de la valeur explicitement donnée à la valeur énumérée qui précède)

    Si l'on avait complètement défini DimentionTrait sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    template <unsigned int I>
    struct DimentionTrait
    {
        enum {value = i};
    };
    nous aurions obtenu le moyen de convertir un entier (non signé) en type différent pour chaque valeur que l'on pouvait donner, car DimensionTrait<1> est différent de DimentionTrait<2>, et, grâce à la valeur énumérée, nous aurions pu récupérer la valeur qui a servi lors de la création du type ( car DimentionTrait<1>::value aurait valut 1 et DimentionTrait<2>::value aurait valut... 2 )

    Nous aurions alors pu définir une classe Vector proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    template <typename DIM>
    class Vector
    {
        /* ... */
       int m_data[DIM::Value];
    };
    et l'utiliser sous une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    Vector<DimentionTrait<3> > tab3Values;
    Vector<DimentionTrait<156> > tab156Values;
    Et nous aurions (selon l'exemple) alors obtenu une instance de Vector permettant d'avoir 3 valeurs pour le premier et une instance de Vector permettant d'avoir 153 valeurs pour le second.

    Mais cela ne nous intéresse pas, par ce que l'on veut:
    • Pouvoir définir des tableaux de 3 valeurs
    • avoir une erreur de compilation si on essaye de créer des Vector contenant un nombre différent de valeurs
    On va donc passer par la déclaration anticipée de DimentionTrait sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    template <unsigned int>
    struct DimentionTrait;
    pour que le compilateur sache qu'il existe une structure nommée DimentionTrait qui se spécialise en fonction d'un entier non signé.

    Il (le compilateur) ne s'étonnera donc pas et acceptera de rencontrer un code proche de DimentionTrait<XXX>, pour aurant que XXX soit une valeur comprise dans l'écart de valeurs admises pour un entier non signé.

    Puis, on va spécialiser (totalement) cette structure pour certaines valeurs (par exemple : 1, 2 et 3 ) en faisant en sorte que, quelle que soit la valeur que l'on utilise, elle soit représentée par un identifiant identique:
    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
     
    //celle ci n'est mise que pour l'explication ;)
    template <unsigned int>
    DimentionTrait<1>
    {
        enum{value = 1};
    };
     
    //celle ci n'est mise que pour l'explication ;)
    template <unsigned int>
    DimentionTrait<2>
    {
        enum{value = 2};
    };
    template <unsigned int>
    DimentionTrait<3>
    {
        enum{value = 3};
    };
    On peut maintenant créer une instance spécialisée pour 1, 2 et trois de DimentionTrait et accéder à son champs "value" sans que le compilateur ne se plaigne:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    cout<<DimentionTrait<1>::value<<endl; // affiche 1
    cout<<DimentionTrait<2>::value<<endl; // affiche 2
    cout<<DimentionTrait<3>::value<<endl; // affiche 3
    Mais, si on essaye d'utiliser une autre valeur que celles pour lesquelles on a fournit une spécialisation, le compilateur va se plaindre du fait que "value n'existe pas pour DimentionTrait<XXX>

    Et c'est, en réalité, ce que l'on voulait, non

    Revenons à notre classe Vector...

    Le type des données et leur nombre doit etre défini à la compilation, tu es donc parti sur un code proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    template <typename REAL, unsigned int D>
    class Vector
    {
    };
    et tu as bien fait.

    Je rajoute (pour la simplicité d'écriture uniquement) un
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    typedef DimentionTrait<D> d_trait;
    qui crée un alias de DimentionTrait, spécialisé pour la valeur de D imbriqué dans Vector.

    Comme je le met en dehors de toute précision de visibilité, ce type est private (car une classe présente l'accessibilité privée par défaut).

    Avec le code
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    enum {dimentionMax = d_trait::value};
    je crée une valeur énumérée (donc, une constante de compilation ) nommée dimentionMax et qui représente... le nombre maximum d'éléments que notre Vector peut obtenir.

    Et c'est là qu'apparait tout le génie de la méta programmation

    En effet, je t'ai dit que le compilateur accepterait (sur base des spécialisations données en exemple) une spécialisation pour 1, 2, et 3 mais se plaindrait si on essayait d'accéder au champs value pour n'importe quelle autre valeur

    Si donc, tu essayes de créer un objet de type Vector<double, 4>, par exemple, DimentionTrait<4>::valuen'existe pas, et donc, lorsque le compilateur rencontrera la ligne enum {dimentionMax = d_trait::value};, il se plaindra que "je suis désolé, mais DimentionTrait<4>::value n'existe pas"

    Comme j'ai mis cette valeur énumérée dans l'accessibilité publique, n'importe qui peut y accéder (de toutes manières, c'est une constante de compilation ) l'utiliser dans des tests ou dans des boucles, et comme une valeur énumérée est équivalente à une variable statique, on peut meme obtenir cette valeur sans passer par un objet particulier du type concerné, à l'instar de la valeur npos de la classe std::string

    Pour être complet, l'idéal est de faire la même chose avec les types qui sont admis comme données pour ta classe vector...

    On partirait d'un trait de politique proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    template <typename T>
    struct Type_trait;
    que l'on spécialiserait pour les différents types admis
    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<>
    struct Type_trait<double>
    {
        typedef double value_type;
        typedef double cref_type;
        typedef double & ref_type;
    };
    template<>
    struct Type_trait<float>
    {
        typedef float value_type;
        typedef float cref_type;
        typedef float & ref_type;
    };
    et que l'on utiliserait dans la classe Vector 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
    template <typename REAL, unsigned int D>
    class Vector
    {
        typedef DimentionTrait<D> d_trait; // voir plus haut
        public:
            enum{dimentionMax =d_trait::value}; //voir plus haut
            typedef Type_trait::value_type value_type;
            /* ... */
        private:
            value_type m_data[dimentionMax];
    };
    car ce que j'ai expliqué au sujet de DimentionTrait<XXX>::value est tout aussi valable pour Type_trait<UNTYPE>::value_type

    Sinon j'ai trouvé une solution satisfaisante pour l'instant : spécialisation totale du template car apparemment ca passe.
    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
    template <typename REAL, unsigned D> class Vector
    {
       REAL m_data[D];
    public:
       Vector<REAL,D> operator ^(const Vector<REAL,D>& p_vector);
    };
     
    template <typename REAL> Vector<REAL,3> crossProduct(const Vector<REAL,3>& p_v1, const Vector<REAL,3>& p_v2)
    {
       ///Faire le taf
    }
     
    template<> Vector<float,3> Vector<float,3>::opertator ^(const Vector<float,3>& p_vector)
    {
       return crossProduct(*this, p_vector);
    }
     
    template<> Vector<double,3> Vector<double,3>::opertator ^(const Vector<double,3>& p_vector)
    {
       return crossProduct(*this, p_vector);
    }
    C'est un solution, en effet... Mais...

    Que se passera-t-il le jour où tu souhaiteras permettre des tableaux à 4 dimensions (ou plus) et / ou des tableaux d'entiers signés, non signés et de complexes

    Tu seras obligé d'ajouter une spécialisation pour chaque combinaison type / valeur, et cela peut faire très rapidement beaucoup de combinaisons

    Ce serait particulièrement dommage, car, quand on y pense, toutes les spécialisations font la même chose

    En passant par des traits de politique, il suffit que tu ajoutes un trait de politique (par exemple, un trait Type_trait<long double> ou DimentionTrait<5> ) pour que tout fonctionne avec le type qui sert pour le trait (ici avec des long double ou avec 5 éléments dans le tableau), et chaque aspect (l'aspect du type utilisé d'un coté et l'aspect du nombre de valeur de l'autre coté) peut évoluer de manière totalement indépendante

    Voilà, j'espère que cela t'aidera à comprendre et à faire évoluer ton niveau ... N'hésites pas à poser des questions, c'est souvent comme cela que l'on s'améliore

  12. #12
    Membre actif Avatar de Rewpparo
    Homme Profil pro
    Amateur
    Inscrit en
    Décembre 2005
    Messages
    170
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Amateur

    Informations forums :
    Inscription : Décembre 2005
    Messages : 170
    Points : 281
    Points
    281
    Par défaut
    Mais c'est le contraire que je veux faire, je ne veux pas empêcher d'instancier des Vector de différents types, je veux au contraire qu'il puisse le faire et que ca soit cohérent.
    Le produit vectoriel n'est défini que pour D==3, il me faut donc juste un moyen pour avoir soit une erreur de compilation, soit de linkage si on fait un produit vectoriel pour D!=3, mais uniquement le produit vectoriel.


    Un ami m'a donné une piste hier, c'est la spécialisation de template de la classe. Si je fais une spécialisation de template pour D==3, je peux rajouter dans l'interface d'autres éléments spécifiques à D==3, je pensais au produit vectoriel, mais également un constructeur.
    Le problème c'est que si je spécialise la classe, je dois recopier toute l'interface. Or elle risque d'évoluer avec le temps, et en avoir des copies n'est pas bon.

    J'ai l'intuition que les traits me permettraient de faire quelque chose du style :
    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
     
    template <typename REAL, unsigned D> class Vector
    {
    public:
       ///interface commune
       Vector();
       Vector<REAL,D> operator+(const Vector<REAL,D>&);
       ...
    };
     
    template <typename REAL> class Vector<REAL,3>
    {
       ///Ici je rajoute uniquement les méthodes en plus
       Vector(REAL x, REAL y, REAL z);
       Vector<REAL,3> operator ^(const Vector<REAL,3>&);
    };
    C'est possible en utilisant des traits (ou une autre magie métaprogramante ?)

  13. #13
    Membre éprouvé
    Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mars 2009
    Messages
    552
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mars 2009
    Messages : 552
    Points : 1 060
    Points
    1 060
    Par défaut
    Bonjour,

    Citation Envoyé par Rewpparo Voir le message
    C'est possible en utilisant des traits (ou une autre magie métaprogramante ?)
    Ca me semble plus simple a faire à l'extérieur de la classe. Tu dois pouvoir faire un truc du genre :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    template < typename REAL >
    Vector< REAL, 3 >   cross( Vector< REAL, 3 > const& a, Vector< REAL, 3 > const& b )
    {
    	return Vector< REAL, 3>( /* */, /* */, /* */ );
    }
     
    template < typename REAL >
    REAL   cross( Vector< REAL, 2 > const& a, Vector< REAL, 2 > const& b )
    {
    	return a[0] * b[1] - a[1] * b[0] ;
    }

  14. #14
    Membre actif Avatar de Rewpparo
    Homme Profil pro
    Amateur
    Inscrit en
    Décembre 2005
    Messages
    170
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Amateur

    Informations forums :
    Inscription : Décembre 2005
    Messages : 170
    Points : 281
    Points
    281
    Par défaut
    C'est ce que j'ai commencé par faire, mais il me dit "invalid use of incomplete type Vector<REAL,3u> with REAL=float". J'ai continué a chercher.

    Apparemment on ne peut pas faire de spécialisation partielle d'un membre d'une classe sans faire une spécialisation partielle identique de l'ensemble de la classe. On peut par contre faire une spécialisation totale d'un membre.
    Si je fais une spécialisation totale de la classe, je suis obligé d'en repréciser tous les membres, ce qui fait un peu lourd.

    La solution à laquelle je suis arrivé, et que je vais garder je pense, c'est de mettre tous les membres communs à tous les D dans une classe Vector_base<REAL,D>, et de créer une nouvelle classe Vector<REAL,D> qui dérive de Vector_base<REAL,D> que je spécialise pour différents D. Ainsi j'ai juste a repréciser les constructeurs, c'est un moindre mal.

    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
    ///Classe contenant tout ce qui est commun a tous les vecteurs
    template <typename REAL, unsigned D> class Vector_base
    {
       ///Buffer pour les coordonnées du vecteur
       REAL m_data[D];
    public:
       ///Constructeur par défaut
       Vector_base();
       ///Constructeur de copie
       Vector_base(const Vector_base& p_vector);
       //Autres membres communs à tous les D, comme opérateur+ etc...
    };
     
    ///La classe qui servira d'interface publique. Implémentation par défaut identique à Vector_base
    template <typename REAL, unsigned D> class Vector : public Vector_base<REAL,D>
    {
    public:
       ///Constructeur par défaut, renvoie a l'implémentation de Vector_base
       Vector() : Vector_base<REAL,D>() {}
       ///Constructeur de copie, renvoie a l'implémentation de Vector_base
       Vector(const Vector_base& p_vector) : Vector_base<REAL,D>(p_vector) {}
    };
     
    ///Spécialisation pour D=3
    template <typename REAL> class Vector<REAL, 3> : public Vector_base<REAL,3>
    {
    public:
       ///Constructeur par défaut, renvoie a l'implémentation de Vector_base
       Vector() : Vector_base<REAL,3>() {}
       ///Constructeur de copie, renvoie a l'implémentation de Vector_base
       Vector(const Vector_base<REAL,3>& p_vector) : Vector_base<REAL,3>(p_vector) {}
       ///Constructeur d'initialisation, typique de D=3
       Vector(REAL p_x, REAL p_y, REAL p_z);  
     
       ///Opérateur produit vectoriel, unique a D=3
       Vector_base<REAL,3> operator ^(const Vector_base<REAL,3>& p_vector);
    };
    Merci à tous de m'avoir aidé dans ma réflexion, même si j'ai pas repris tel quel ca m'a fait phosphorer ! Vous entendez surement parler de mes vecteurs a nouveau quand j'attaquerais les templates d'expression

  15. #15
    Membre éprouvé
    Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mars 2009
    Messages
    552
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mars 2009
    Messages : 552
    Points : 1 060
    Points
    1 060
    Par défaut
    Citation Envoyé par Rewpparo Voir le message
    La solution à laquelle je suis arrivé, et que je vais garder je pense, c'est de mettre tous les membres communs à tous les D dans une classe Vector_base<REAL,D>, et de créer une nouvelle classe Vector<REAL,D> qui dérive de Vector_base<REAL,D> que je spécialise pour différents D. Ainsi j'ai juste a repréciser les constructeurs, c'est un moindre mal.
    Heu, si tu ne fais pas du CRTP, tes opérateurs vont faire des copies dans tous les sens...

    Sinon, l'erreur que tu as me fait dire que tu as déclaré la template, mais qu'elle n'est pas incluse lors de la compilation de ton produit vectoriel.

    Tu m'as mis le doute du coup, mais la chose suivante fonctionne (désolé, je te laisse mon bordel avec et je ne spécialise pas pour un contrôle la compilation des constructeurs ) :

    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
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
     
    /**
     * 2D cross fonction
     */
    template < typename T, size_t N >
    T squared_distance( Vector< T, N > const& a, Vector< T, N > const& b )
    {
    	T result = T(0);
    	for ( size_t i = 0; i < N; i++ ){
    		result += ( b[i] - a[i] )*( b[i] - a[i] ) ;
    	}
    	return result ;
    }
     
    /**
     * 2D cross fonction
     */
    template < typename T >
    T cross( Vector< T, 2 > const& a, Vector< T, 2 > const& b )
    {
    	return a[0]*b[1] - a[1] * b[0] ;
    }
    /**
     * 3D cross fonction
     */
    template < typename T >
    Vector< T, 3 > cross( Vector< T, 3 > const& a, Vector< T, 3 > const& b )
    {
    	return Vector< T, 3 >(
    		a[1]*b[2] - a[2] * b[1],
    		a[2]*b[0] - a[0] * b[2],
    		a[0]*b[1] - a[1] * b[0]
    	);
    }
     
    const double ABSOLUTE_EPSILON = 1.0e-10 ;
     
     
    BOOST_AUTO_TEST_SUITE( TestVector )
     
    BOOST_AUTO_TEST_CASE( testDistance )
    {
    	Vector< double, 2 > origine;
    	Vector< double, 2 > uy(3.0,4.0);
     
    	BOOST_CHECK_CLOSE( squared_distance( uy, origine ), 25.0, ABSOLUTE_EPSILON );
    }
     
    BOOST_AUTO_TEST_CASE( test2D )
    {
    	Vector< double, 2 > ux(1.0,0.0);
    	Vector< double, 2 > uy(0.0,1.0);
     
    	BOOST_CHECK_CLOSE( cross( ux, uy ), 1.0, ABSOLUTE_EPSILON );
    }
     
    BOOST_AUTO_TEST_CASE( test3D )
    {
    	Vector< double, 3 > ux(1.0,0.0,0.0);
    	Vector< double, 3 > uy(0.0,1.0,0.0);
    	Vector< double, 3 > uz(0.0,0.0,1.0);
     
    	BOOST_CHECK_SMALL( squared_distance( cross( ux, uy ), uz ), ABSOLUTE_EPSILON );
    }
     
    BOOST_AUTO_TEST_CASE( testNonInstanciable )
    {
    	Vector< double, 4 > ux; ux[0] = 1.0 ;
    	Vector< double, 4 > uy; uy[1] = 1.0 ;
     
    	//provoque bien une erreur
    	//cross( ux, uy );
     
    	//TempTest.cpp:192:16: error: no matching function for call to 'cross(Vector<double, 4u>&, Vector<double, 4u>&)'
    	//TempTest.cpp:192:16: note: candidates are:
    	//TempTest.cpp:140:3: note: template<class T> T cross(const Vector<T, 2u>&, const Vector<T, 2u>&)
    	//TempTest.cpp:148:16: note: template<class T> Vector<T, 3u> cross(const Vector<T, 3u>&, const Vector<T, 3u>&)
    }
     
     
     
    BOOST_AUTO_TEST_SUITE_END()

    PS : Si tu acceptes d'avoir des fabriques plutôt que des constructeurs, tu n'as plus besoin de faire une hiérarchie et tu as le contrôle à la compilation.

    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
     
    template < typename T >
    Vector< T, 2 > createVector( T const& x, T const& y )
    {
    	Vector< T, 2 > result ;
    	result[0] = x ;
    	result[1] = y ;
    	return result ;
    }
    template < typename T >
    Vector< T, 3 > createVector( T const& x, T const& y, T const& z )
    {
    	Vector< T, 3 > result ;
    	result[0] = x ;
    	result[1] = y ;
    	result[2] = z ;
    	return result ;
    }

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 629
    Points : 30 692
    Points
    30 692
    Par défaut
    Rien que pour toi, je me suis replongé dans mes archives, car je savais avoir écrit une prose dans laquelle j'expliquais un peut le raisonnement que j'avais suivi pour la création de traits et de politiques...

    Ce n'est peut etre pas tout à fait adapté à ce que tu cherches, mais, si cela te permet déjà de comprendre le principe, cela ne sera pas perdu

    Ma prose se trouve ici ... n'hésite pas à poser des questions

  17. #17
    Membre actif Avatar de Rewpparo
    Homme Profil pro
    Amateur
    Inscrit en
    Décembre 2005
    Messages
    170
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Amateur

    Informations forums :
    Inscription : Décembre 2005
    Messages : 170
    Points : 281
    Points
    281
    Par défaut
    Citation Envoyé par bretus Voir le message
    Heu, si tu ne fais pas du CRTP, tes opérateurs vont faire des copies dans tous les sens...
    Bien vu, je fais ca demain.

    Citation Envoyé par bretus Voir le message
    Sinon, l'erreur que tu as me fait dire que tu as déclaré la template, mais qu'elle n'est pas incluse lors de la compilation de ton produit vectoriel.
    J'ai bien vérifié c'est inclus. De plus, si remplace ma spécialisation partielle par une spécialisation complète ca compile.

    Citation Envoyé par bretus Voir le message
    Tu m'as mis le doute du coup, mais la chose suivante fonctionne
    Dans ce cas la oui ca marche, car la fonction (ou opérateur) n'est pas dans la classe. C'est ce que j'avais fait avant, mais je voulais mettre un maximum d'opérateurs dans la classe afin de simplifier les instanciations explicites.

    Citation Envoyé par bretus Voir le message
    PS : Si tu acceptes d'avoir des fabriques plutôt que des constructeurs, tu n'as plus besoin de faire une hiérarchie et tu as le contrôle à la compilation.
    Ca aussi c'est ce que j'avais avant, mais la encore je veux un max de trucs dans la classe. De plus il reste toujours le produit vectoriel, et d'autres méthodes qui sont typiques d'un D en particulier. Y compris des constructeurs (Construire une matrice depuis un quaternion, ca marche pour D=3, depuis un angle pour D=2).


    Citation Envoyé par koala01 Voir le message
    Ma prose se trouve ici ... n'hésite pas à poser des questions
    Merci, j'ai bookmarké pas le temps de tout lire ce soir je t'en reparle si j'ai des questions.

  18. #18
    Membre actif Avatar de Rewpparo
    Homme Profil pro
    Amateur
    Inscrit en
    Décembre 2005
    Messages
    170
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Amateur

    Informations forums :
    Inscription : Décembre 2005
    Messages : 170
    Points : 281
    Points
    281
    Par défaut
    Citation Envoyé par rewpparo
    Citation Envoyé par bretus Voir le message
    Heu, si tu ne fais pas du CRTP, tes opérateurs vont faire des copies dans tous les sens...
    Bien vu, je fais ca demain.
    J'ai opté pour une variante que je pense intéressante. J'ai mis une forward declaration de Vector avant Vector_base, et j'ai donc mis des Vector partout dans les opérateurs. Vu que j'avais des static_cast<Vector&>(*this) dans Vector_base, il fallait que je sois sur que toutes les instances de Vector_base soient des Vector. J'ai donc mis le constructeur privé, et friend Vector.
    Comme ca Vector_base ne peut pas être instancié, a part par Vector.

    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 <typename REAL, unsigned D> class Vector;
     
    template <typename REAL, unsigned D> class Vector_base
    {
       friend class Vector<REAL,D>;
       Vector_base();
       ...
    public:
       ...
    };
     
    template <typename REAL, unsigned D> class Vector : public Vector_base<REAL,D>
    {
       ...
    };

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

Discussions similaires

  1. Réponses: 1
    Dernier message: 12/11/2013, 13h22
  2. Factorisation des membres d'une classe template
    Par oodini dans le forum Langage
    Réponses: 17
    Dernier message: 28/08/2013, 12h12
  3. Spécialisation de greater pour une classe template
    Par oodini dans le forum Langage
    Réponses: 4
    Dernier message: 16/11/2012, 09h31
  4. Réponses: 9
    Dernier message: 12/07/2010, 14h25
  5. Réponses: 0
    Dernier message: 25/07/2007, 14h47

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