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 :

Template, oublirais-je quelque chose?


Sujet :

Langage C++

  1. #1
    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 611
    Points
    30 611
    Par défaut Template, oublirais-je quelque chose?
    Salut,

    Pour une fois, c'est moi qui vient avec un problème de template.
    Soit une classe de base "classique" sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class Base{
        public:
            Base(){}
           /* interdisons la copie et l'affectation (C++11 inside) */
            Base(Base const &) = delete;
            Base & operator = (Base const&) = delete;
            /* défini dans le cpp, ne fait strictement rien */
            virtual ~Base(); 
            virtual void foo() = 0;
            virtual void bar() = 0;
    };
    A laquelle on rajoute deux classe template, l'une sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    template <typename T1, typename T2>
    class Derivee1;
    template <typename T1>
    class Derivee2;
    et deux tag simples
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    struct tag1{};
    struct tag2{};
    Si je définis Derivee1 sous la forme de
    derivee1.h
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    template <typename T1, typename T2>
    class Derivee1 : public Base{
        public:
            void foo();
            void bar();
    };
    je peux déporter la spécialisation (partielle ou totale) de foo dans un fichier d'instanciation explicit:
    derivee_explicit.cpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    template <>
    void Derivee1<tag1, tag2>::foo(){
        std::cout<<"ca marche";
    }
    template <>
    void Derivee1<tag1, tag2>::bar(){
        std::cout<<"Si ca va pour l'un, ca va pour l'autre";
    }
    template class Derivee1<tag1, tag2>;
    [EDIT]
    Notez que cela fonctionne aussi très bien avec une spécialisation partielle : si je définissait template<typename T1> class Derivee1<T1,tag2>, ca marcherrait
    [/EDIT]
    Si je fournis directement une spécialisation totale sous une forme proche de
    Derive2OK.h
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    template<>
    class Derivee2<tag1> : public Base{
        public:
           Derivee2(){}
           void foo(){
               std::cout<<"ca marche";
           }
           void bar(){
                std::cout<<"Si ca va pour l'un, ca va pour l'autre";
           }
    };
    Par contre, si j'essaye de mixer les deux (spécialisation totale et export de la fonction)
    Derivee2KO.h
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    template<>
    class Derivee2<tag2> : public Base{
        public:
           Derivee1(){}
           void foo();
           void bar();
    };
    derivee2_explicit.cpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    template <>
    void Derivee<tag2>::foo(){
        std::cout<<"Ben non, justement";
    }
    template <>
    void Derivee1<tag2>::bar(){
        std::cout<<"Le compilateur ne trouve pas les prototypes";
    }
    template class Derivee2<tag2>;
    Je dois avoir oublié quelque chose, mais quoi
    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

  2. #2
    Membre expert
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2011
    Messages
    739
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

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

    Informations forums :
    Inscription : Juin 2011
    Messages : 739
    Points : 3 627
    Points
    3 627
    Par défaut
    Salut,

    À la fin les exemples partent en cacahuète
    Derivee ce transforme en Derive (un 'e' en moins) et un des deux types disparaît et ce fait remplacer par une valeur template<tag1> class Derive1<tag2>.
    Ne serait-ce pas plutôt template<typename T> class Derive1<T, tag2> ?

    J'ai l'impression que le pot de pinceaux est tout mélangé ^^.

  3. #3
    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 611
    Points
    30 611
    Par défaut
    corrigé...

    J'ai été un peu vite pour relire

    Mais le problème reste : si in définit une spécialisation partielle ou aucune, ca marche alors que si l'on essaye de définir une spécialisation totale, ca va plus...
    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

  4. #4
    Membre expert
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2011
    Messages
    739
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

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

    Informations forums :
    Inscription : Juin 2011
    Messages : 739
    Points : 3 627
    Points
    3 627
    Par défaut
    Juste avec ceci ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    void Derivee2<tag2>::foo(){
        std::cout<<"Ben non, justement";
    }
     
    void Derivee2<tag2>::bar(){
        std::cout<<"Le compilateur ne trouve pas les prototypes";
    }

  5. #5
    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 611
    Points
    30 611
    Par défaut
    Citation Envoyé par jo_link_noir Voir le message
    Juste avec ceci ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    void Derivee2<tag2>::foo(){
        std::cout<<"Ben non, justement";
    }
     
    void Derivee2<tag2>::bar(){
        std::cout<<"Le compilateur ne trouve pas les prototypes";
    }
    Ce serait trop beau, mais non

    Mais, surtout, ce que j'aimerais, c'est qu'on me rappelle pourquoi ca ne veut pas va
    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

  6. #6
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 113
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 113
    Points : 32 951
    Points
    32 951
    Billets dans le blog
    4
    Par défaut
    As-tu regardé l'implémentation de std::vector<bool> ? Puisqu'il s'agit d'une spécialisation peut-être que tu pourras y trouver des infos utilisables.

    Cela dit, je viens de tester ça (ça ressemble à ce que tu essayes de faire on dirait) et:

    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
    struct e1 { enum {}; };
    struct e2 { enum {}; };
    struct Base { virtual void foo()=0; virtual void bar()=0; };
    template<class T> struct Deriv : Base { void foo() { std::cout<<"foo<T>"<<std::endl; } void bar() { std::cout<<"bar<T>"<<std::endl; } };
    template<> struct Deriv<e1> : Base { void foo(); void bar(); };
    template<> void Deriv<e1>::foo() { std::cout<<"foo<e1>"<<std::endl; }
    template<> void Deriv<e1>::bar() { std::cout<<"bar<e1>"<<std::endl; }
    void main()
    {
    	Deriv<e2> a;
    	a.foo();
    	a.bar();
    	Deriv<e1> b;
    	b.foo();
    	b.bar();
    }
    génère une C2910

    Mais
    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
    struct e1 { enum {}; };
    struct e2 { enum {}; };
    struct Base { virtual void foo()=0; virtual void bar()=0; };
    template<class T> struct Deriv : Base { void foo() { std::cout<<"foo<T>"<<std::endl; } void bar() { std::cout<<"bar<T>"<<std::endl; } };
    template<> struct Deriv<e1> : Base { void foo(); void bar(); };
    /*template<>*/ void Deriv<e1>::foo() { std::cout<<"foo<e1>"<<std::endl; }
    /*template<>*/ void Deriv<e1>::bar() { std::cout<<"bar<e1>"<<std::endl; }
    void main()
    {
    	Deriv<e2> a;
    	a.foo();
    	a.bar();
    	Deriv<e1> b;
    	b.foo();
    	b.bar();
    }
    Fonctionne très bien
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  7. #7
    Membre éclairé

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2007
    Messages
    373
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : Royaume-Uni

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Santé

    Informations forums :
    Inscription : Juin 2007
    Messages : 373
    Points : 764
    Points
    764
    Par défaut
    J'ai peut être mal compris ton problème, mais si je me ramène au strict nécessaire (et que je corrige les quelques erreurs qui restent) :

    base.hpp
    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
    class Base{
        public:
            Base(){}
           /* interdisons la copie et l'affectation (C++11 inside) */
            Base(Base const &) = delete;
            Base & operator = (Base const&) = delete;
            /* défini dans le cpp, ne fait strictement rien */
            virtual ~Base() {}
            virtual void foo() = 0;
            virtual void bar() = 0;
    };
     
    template <typename T1>
    class Derivee2;
     
    struct tag2{};
     
    template<>
    class Derivee2<tag2> : public Base{
        public:
           Derivee2(){}
           void foo();
           void bar();
    };
    explicit.cpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    #include "base.hpp"
     
    void Derivee2<tag2>::foo(){
        std::cout<<"Ben si, justement";
    }
     
    void Derivee2<tag2>::bar(){
        std::cout<<"Le compilateur trouve bien les prototypes";
    }
    main.cpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    #include "base.hpp"
     
    int main(int argc, char* argv[]) {
        Derivee2<tag2> t;
        t.foo();
        t.bar();
     
        return 0;
    }
    Alors ça compile très bien (testé avec g++ et clang) :
    g++ -std=c++11 explicit.cpp -c -o explicit.o
    g++ -std=c++11 main.cpp -c -o main.o
    g++ -std=c++11 explicit.o main.o -o test

  8. #8
    Membre expert
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2011
    Messages
    739
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

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

    Informations forums :
    Inscription : Juin 2011
    Messages : 739
    Points : 3 627
    Points
    3 627
    Par défaut
    Je cherchais justement le paragraphe correspond dans la norme et c'est pourtant ce qui est marqué.

    14.7.3: Explicit specialization (petit 5)

    A member of an explicitly specialized class is not implicitly instantiated from the member declaration of the class template; instead, the member of the class template specialization shall itself be explicitly defined if its definition is required. In this case, the definition of the class template explicit specialization shall be in scope at the point at which the member is defined. The definition of an explicitly specialized class is unrelated to the definition of a generated specialization. That is, its members need not have the same names, types, etc. as the members of a generated specialization. Members of an explicitly specialized class template are defined in the same manner as members of normal classes, and not using the template<> syntax. The same is true when defining a member of an explicitly specialized member class. However, template<> is used in defining a member of an explicitly specialized member class template that is specialized as a class template.
    [ Example:

    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
     
    template<class T> struct A {
      struct B { };
      template<class U> struct C { };
    };
     
    template<> struct A<int> {
      void f(int);
    };
     
    void h() {
      A<int> a;
      a.f(16);
    }
     
    // A<int>::f must be defined somewhere
    // template<> not used for a member of an
    // explicitly specialized class template
    void A<int>::f(int) { /* ... */ }
     
    template<> struct A<char>::B {
      void f();
    };
     
    // template<> also not used when defining a member of
    // an explicitly specialized member class
    void A<char>::B::f() { /* ... */ }
    template<> template<class U> struct A<char>::C {
      void f();
    };
     
    // template<> is used when defining a member of an explicitly
    // specialized member class template specialized as a class template
    template<>
    template<class U> void A<char>::C<U>::f() { /* ... */ }
     
    template<> struct A<short>::B {
      void f();
    };
     
    template<> void A<short>::B::f() { /* ... */ }
    // error: template<> not permitted
    template<> template<class U> struct A<short>::C {
      void f();
    };
     
    template<class U> void A<short>::C<U>::f() { /* ... */ }
    // error: template<> required
    — end example ]

    Et dans mon test ça passe. Je le le post, dit le moi si je suis à côté de la plaque.
    main.cpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    #include "derivee.hpp"
     
    int main ()
    {
      Derivee2<tag1>().foo();
      Derivee2<tag2>().foo();
    }
    derivee.hpp
    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
    #ifndef DERIVEE_HPP
    #define DERIVEE_HPP
    #include <iostream>
     
    class Base{
    public:
      Base(){}
      /* interdisons la copie et l'affectation (C++11 inside) */
       Base(Base const &) = delete;
       Base & operator = (Base const&) = delete;
      /* défini dans le cpp, ne fait strictement rien */
      virtual ~Base(){};
      virtual void foo() = 0;
      virtual void bar() = 0;
    };
     
    struct tag1{};
    struct tag2{};
     
    template <typename T1>
    class Derivee2 : public Base{
    public:
      Derivee2(){}
      void foo(){
        std::cout<<"ca marche";
      }
      void bar(){
        std::cout<<"Si ca va pour l'un, ca va pour l'autre";
      }
    };
     
    template<>
    class Derivee2<tag1> : public Base{
    public:
      Derivee2(){}
      void foo();
      void bar();
    };
     
    #endif
    derivee_explicit.cpp:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    #include "derivee.hpp"
    void Derivee2<tag1>::foo(){
      std::cout<<"Ben non, justement";
    }
    void Derivee2<tag1>::bar(){
      std::cout<<"Le compilateur ne trouve pas les prototypes";
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    $ g++ -std=c++11 derivee_explicit.cpp -c
    $ g++ -std=c++11 main.cpp -c
    $ g++ main.o derivee_explicit.o
    $ ./a.out
    > Ben non, justementca marche

  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 611
    Points
    30 611
    Par défaut
    En fait, mes explications apportaient les deux solutions qui fonctionnent, mais le but en définitive était de savoir si j'avais affaire à une comportement défini par la norme ou si c'était un bug du compilateur face à une situation proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    class Base{
       /* tout ce qu'il faut pour une vraie classe ayant sémantique d'entité */
    };
    template <typename T/*,typename T2 */>
    class Derivee;
    dans un autre fichier d'en-tête
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    template <>
    class Derivee <UnType/*,UnAutreType*/> : public Base{
        public:
            void foo();
            /* autres fonctions */
        private:
           /* membres spécifiques à la spécialisation */
    };
    dans un fichier d'implémentation
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    /* template <>*/
    void Derivee<UnType/*,UnAutreType*/>::foo(){
     
    }
    /* autres fonctions */
    template class Derivee<UnType/*,UnAutreType*/>; //ca, ca marche, c'est sur
    Et la réponse est bien (merci jo_link_noir d'avoir fait la recherche):
    On doit choisir entre fournir une définition complète non spécialisée de la classe et définir les spécialisations spécifiques de manière déportée OU ne pas fournir de définition complète de la classe non spécialisée et définir les fonctions directement dans la définition.

    Par contre, jo_link_noir, l'exemple que tu donnes (basé sur le mien) ne fonctionne que parce que tu as déjà défini l'ensemble de la classe dérivée sans spécialisation Si tu n'avais fournis qu'une déclaration anticipée pour Derivee, tu aurais été obligé de définir les fonctions foo et bar dans la spécialisation de cette dernière .

    Enfin, le problème est donc résolu, merci à tous les deux
    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

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

Discussions similaires

  1. Réponses: 23
    Dernier message: 22/12/2012, 04h16
  2. [thread][methodologie]Quelque chose que je ne comprends pas!
    Par norkius dans le forum Général Java
    Réponses: 5
    Dernier message: 16/03/2005, 15h01
  3. Réponses: 3
    Dernier message: 27/04/2004, 19h21

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