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 :

Des template qui n'en font qu'à leur tête


Sujet :

C++

  1. #1
    Membre confirmé
    Avatar de Captain'Flam
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2011
    Messages
    273
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

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

    Informations forums :
    Inscription : Février 2011
    Messages : 273
    Points : 455
    Points
    455
    Billets dans le blog
    1
    Par défaut Des template qui n'en font qu'à leur tête
    Bonjour,

    voici un petit bout de code dont je ne m'explique pas le comportement.

    Mon but initial était d'avoir une fonction template avec une version spécialisée pour les types "simples" (ie non pointés), les pointeurs et les char* en particulier.
    Voici le code :
    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
    class Foo
        {
        public:
            Foo ( int x , int y ) : x(x),y(y) {}
            template <class OUT> friend OUT & operator<< ( OUT & output , const Foo & f )
                {
                output << f.x << ',' << f.y ;
                return output ;
                }
        protected:
            int x,y ;
        };
     
    #include <iostream>
    using namespace std ;
     
    template<class T>           T Bare ( T x )            { cout << "simple    : " ; return  x ;}
    template<class T>           T Bare ( T * x )          { cout << "pointer   : " ; return *x ;}
    template<>              char* Bare ( char * x )       { cout << "char*     : " ; return  x ;}
    template<>        const char* Bare ( const char * x ) { cout << "cst char* : " ; return  x ;}
     
    template<class T>        void Spam ( T x )            { cout << "simple    : " <<  x << endl ;}
    template<class T>        void Spam ( T * x )          { cout << "pointer   : " << *x << endl ;}
    template<>               void Spam ( char * x )       { cout << "char*     : " <<  x << endl ;}
    template<>               void Spam ( const char * x ) { cout << "cst char* : " <<  x << endl ;}
     
    template<class T> void SuperSpam ( T x ) { cout << Bare( x ) << "  ---> " ; Spam( x ) ;}
     
    int main ()
        {
        const char s [] = "0123456789" ;
        int  x = 123 ;
        const int * y = &x ;
        Foo   f( 465,789 ) ;
        SuperSpam( 3 ) ;
        SuperSpam( y ) ;
        SuperSpam( f ) ;
        SuperSpam( &f ) ;
        SuperSpam( "text" ) ;
        SuperSpam( s+3 ) ;
        }
    la sortie ressemble à ça
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    simple    : 3  ---> simple    : 3
    pointer   : 123  ---> pointer   : 123
    simple    : 465,789  ---> simple    : 465,789
    pointer   : 465,789  ---> pointer   : 465,789
    pointer   : t  ---> cst char* : text
    pointer   : 3  ---> cst char* : 3456789
    Ma question :
    pourquoi Bare ne reconnaît pas les char* (ils sont assimilés à des pointeurs "ordinaires") alors que Spam fait bien la différence ?

    Mystère et boucle de gomme++
    Captain'Flam
    anciennement Sopsag, aka Hadrien
    Win seven x64 & Win 10 / Visual 2017 / Python 2.7 / Eclipse

  2. #2
    En attente de confirmation mail

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : France, Doubs (Franche Comté)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Points : 3 311
    Points
    3 311
    Par défaut
    Bonjour,

    Oublies les spécialisations de fonction et utilises la surcharge :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    template<class T>           T Bare ( T x )            { cout << "simple    : " ; return  x ;}
    template<class T>           T Bare ( T * x )          { cout << "pointer   : " ; return *x ;}
    char* Bare ( char * x )       { cout << "char*     : " ; return  x ;}
    const char* Bare ( const char * x ) { cout << "cst char* : " ; return  x ;}
     
    template<class T>        void Spam ( T x )            { cout << "simple    : " <<  x << endl ;}
    template<class T>        void Spam ( T * x )          { cout << "pointer   : " << *x << endl ;}
    void Spam ( char * x )       { cout << "char*     : " <<  x << endl ;}
    void Spam ( const char * x ) { cout << "cst char* : " <<  x << endl ;}
    Pour l'explication, pour Bare les spécialisations concernent la première fonction alors que pour Spam elles concernent la seconde. La résolution de l'appel tente d'appeler la seconde fonction dans les deux cas, sauf que dans un il existe une spécialisation pour cette fonction et pas dans l'autre. Le plus simple est encore de ne pas utiliser la spécialisation de fonction et de préférer la surcharge.

  3. #3
    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
    Le mieux reste de ne pas faire de fausses fonction template au risque d'avoir des fonctions impossibles à appeler sans cast.

    Typiquement, si tu as template<> pour une fonction, alors enlève le. Ici le raisonnement du compilateur est le suivant:
    - prendre les protos avec des arguments compatibles (T Bare(T*) et T Bare(T)).
    - déduire les priorités pour ne garder que T Bare(T*) qui à une qualification plus forte (on dit prendre un pointeur)
    - Vérifier si une spécialisation existe et match: il n'y en a pas car aucune spécialisation.

    La fonction const char * Bare(const char *) est en fait une spécialisation de T Bare(T) pour T = const char *.

    EDIT: On n'est plus prévenu quand un message est posté pendant l’écriture d'une réponse ?

  4. #4
    Membre confirmé
    Avatar de Captain'Flam
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2011
    Messages
    273
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

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

    Informations forums :
    Inscription : Février 2011
    Messages : 273
    Points : 455
    Points
    455
    Billets dans le blog
    1
    Par défaut
    Merci à vous chers cépépistes,

    en effet, en évitant les "template<>" j'obtiens ce que je veux.
    Mais je dois admettre que je ne comprends pas bien vos explications...
    Notamment pourquoi Spam se comporte comme je l'attends, alors que Bare non.
    La seule différence entre les 2 est le type de retour...

    Hadrien
    Captain'Flam
    anciennement Sopsag, aka Hadrien
    Win seven x64 & Win 10 / Visual 2017 / Python 2.7 / Eclipse

  5. #5
    En attente de confirmation mail

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : France, Doubs (Franche Comté)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Points : 3 311
    Points
    3 311
    Par défaut
    Je vais numéroter les fonction :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    template<class T>           T Bare ( T x )             (1)
    template<class T>           T Bare ( T * x )          (2)
    template<> char* Bare ( char * x )                    (3)
    template<> const char* Bare ( const char * x )   (4)
     
    template<class T>        void Spam ( T x )           (5)
    template<class T>        void Spam ( T * x )        (6)
    template<> void Spam ( char * x )                     (7)
    template<> void Spam ( const char * x )             (8)
    En réalité les lignes 3 et 4 sont une spécialisation de la ligne 1 et les lignes 7 et 8 une spécialisation de la ligne 6. Quand le compilateur résout un appel, il ne considère pas les spécialisations au moment de choisir la fonction template qui correspond le mieux, il doit donc choisir entre :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    template<class T>           T Bare ( T x )             (1)
    template<class T>           T Bare ( T * x )          (2)
     
    template<class T>        void Spam ( T x )           (5)
    template<class T>        void Spam ( T * x )        (6)
    Dans l'utilisation qui te pose problème c'est les lignes 2 et 6 qui correspondent le mieux, ensuite il regarde si il y a une spécialisation pour cette fonction, pour 2 non, donc il instancie directement 2, pour 6 oui c'est la ligne 8 qu'il utilise donc, d'où la différence de comportement.

    Quand tu enlèves les template<>, ce ne sont plus des spécialisations mais des surcharges, le compilateur doit directement choisir entre les 4 lignes.

  6. #6
    Membre confirmé
    Avatar de Captain'Flam
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2011
    Messages
    273
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

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

    Informations forums :
    Inscription : Février 2011
    Messages : 273
    Points : 455
    Points
    455
    Billets dans le blog
    1
    Par défaut
    Merci Flob90 de te donner tant de mal pour faire rentrer les raffinements du c++ dans mon cerveau ramolli... mais je ne comprends toujours pas

    Pourquoi 3 et 4 (char* et const char*) sont des spécialisations de 1 (T)
    alors que 7 et 8 (char* et const char*) sont des spécialisations de 6 (T*) ?

    Je ne vois toujours pas pourquoi il y a une différence entre Bare et Spam qui ont des signatures rigoureusement identiques (au type de retour près) et qui sont appelées avec les même paramètres...

    En revanche j'ai bien compris le problème lié au choix du compilateur : d'abord parmi les surcharges, puis parmi les spécialisations des templates.
    Captain'Flam
    anciennement Sopsag, aka Hadrien
    Win seven x64 & Win 10 / Visual 2017 / Python 2.7 / Eclipse

  7. #7
    Invité
    Invité(e)
    Par défaut
    Bonjour,

    Parce que pour Bare, tu as template<class T> T Bare ( T * x ) : un pointeur T* en entrée, MAIS un objet T en sortie.

    Supposons que template<> const char* Bare ( const char * x ) soit une spécialisation de (2) template<class T> T Bare ( T * x ) :
    Alors nous aurions T = quoi dans (2) ?
    const char ? => const char Bare ( const char * x ) il manque le * en retour pour correspondre à ta "spécialisation" ;
    const char * ? => const char * Bare ( const char * * x ) un * en trop en paramètre.
    Il n'a pas de déduction possible, template<> const char* Bare ( const char * x ) est donc la spécialisation de (1) template<class T> T Bare ( T x ) avec T = const char *.

    La différence se situe au niveau du retour, pour Spam, cette question d'équivalence paramètre d'entrée/retour ne se pose pas.

  8. #8
    Expert éminent sénior
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Février 2005
    Messages
    5 069
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Val de Marne (Île de France)

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

    Informations forums :
    Inscription : Février 2005
    Messages : 5 069
    Points : 12 113
    Points
    12 113
    Par défaut
    Merci à Captain'Flam d'avoir posé la question, car je ne comprenais pas non plus.
    Une énorme différence, c'est l'absence de '*' dans le type de retour de la fonction en (2), "T" et non "T *".
    C'est cette absence qui fait que (3) et (4) spécialise (1) et non (2).

  9. #9
    Membre confirmé
    Avatar de Captain'Flam
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2011
    Messages
    273
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

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

    Informations forums :
    Inscription : Février 2011
    Messages : 273
    Points : 455
    Points
    455
    Billets dans le blog
    1
    Par défaut
    Argl !

    (au type de retour près)
    J'avais délibérément mis de côté la clef du mystère...

    Merci à vous je me coucherai moins bête !
    Captain'Flam
    anciennement Sopsag, aka Hadrien
    Win seven x64 & Win 10 / Visual 2017 / Python 2.7 / Eclipse

  10. #10
    En attente de confirmation mail

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : France, Doubs (Franche Comté)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Points : 3 311
    Points
    3 311
    Par défaut
    Pour (3) et (4) la réponse est assez évidente, aucun T de (2) ne peut donner ces signatures, seul la (1) avec T=char (ou const char) convient.

    Pour (7) et (8), j'ai dit une (petite) bétise, en réalité elles sont des spécialisations à la fois de (5) et de (6). Par exemple tu obtiens (7) avec (5) en prenant T=char* mais aussi avec (6) en prenant T=char. (voir message de Jean-Marc).

    Ca ne change rien à la suite du raisonnement, (2) et (6) sont choisis, (2) n'a pas de spécialisation donc elle est utilisé ainsi, (6) a la spécialisation (8) qui correspond, elle est utilisée.

    Si la question est pour (2) et (6) sont choisis, la raison est que pour appeler (2) et (6), T est déduit à const char, alors que pour (1) et (5) c'est déduit à const char*, le compilateur prend la déduction "la plus simple" : const char est plus simple que const char*.

    PS: La signature d'une fonction template inclus son type de retour.

  11. #11
    Expert éminent

    Inscrit en
    Novembre 2005
    Messages
    5 145
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 5 145
    Points : 6 911
    Points
    6 911
    Par défaut
    Citation Envoyé par Flob90 Voir le message
    Pour (7) et (8), j'ai dit une (petite) bétise, en réalité elles sont des spécialisations à la fois de (5) et de (6).
    7 et 8 sont des spécialisations de 6. Pour preuve, exécuter

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Spam<char const*>("text");
    Si 6 n'était pas visible, ce serait en effet des spécialisations de 5 (les règles de déduction est le même pour la spécialisation et l'appel). Pour spécialiser 5 tout en ayant 6 visible, il faut utiliser la syntaxe qui donne les arguments template explicitement plutôt que de les déduire:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    template<> void Spam<const char*>(const char * x) { cout << "cst char!* : " <<  x << endl; }
    Les MP ne sont pas là pour les questions techniques, les forums sont là pour ça.

  12. #12
    En attente de confirmation mail

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : France, Doubs (Franche Comté)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Points : 3 311
    Points
    3 311
    Par défaut
    En effet, bourde de ma part, j'avais testé un truc avec un enable_if qui me faisait penser qu'on pouvait avoir une spécialisation se référant à deux déclarations, mais si les règles de déduction sont les même, alors ça ne montrait rien. J'ai cherché la partie de la norme qui dit : "les règles de déduction est le même pour la spécialisation et l'appel" mais j'ai pas vraiment trouvé, ou "une spécialisation se réfère à une seule déclaration". Si tu as le passage je suis preneur (c'est la 14.8.2.6 ?).

    Comme contre exemple, celui-ci (qui donne une erreur dans clang, mais c'est le but) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    template<class T, class U>
    void foo(T,U*){}
     
    template<class T, class U>
    void foo(T*,U){}
     
    template<>
    void foo(int*,int*){}
    Que je viens de tester me convainc aussi.

  13. #13
    Expert éminent

    Inscrit en
    Novembre 2005
    Messages
    5 145
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 5 145
    Points : 6 911
    Points
    6 911
    Par défaut
    Citation Envoyé par Flob90 Voir le message
    J'ai cherché la partie de la norme qui dit : "les règles de déduction est le même pour la spécialisation et l'appel" mais j'ai pas vraiment trouvé, ou "une spécialisation se réfère à une seule déclaration". Si tu as le passage je suis preneur (c'est la 14.8.2.6 ?).
    Ca semble ca.
    Les MP ne sont pas là pour les questions techniques, les forums sont là pour ça.

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

Discussions similaires

  1. Des div qui sortent de leur parent?
    Par creamille dans le forum Mise en page CSS
    Réponses: 1
    Dernier message: 11/06/2008, 17h57
  2. Des div qui font ceux qu'ils veulent
    Par sacados1 dans le forum Mise en page CSS
    Réponses: 3
    Dernier message: 20/11/2007, 22h47
  3. [débutant] Mes controles n'ent font qu'à leur tête!
    Par Alouka dans le forum Visual C++
    Réponses: 3
    Dernier message: 25/10/2006, 10h17
  4. Onglets qui n'en font qu'à leur tête !
    Par bitou11 dans le forum Access
    Réponses: 2
    Dernier message: 01/08/2006, 16h45

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