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 :

Instanciation de fonction template et temporaire


Sujet :

Langage C++

  1. #1
    Membre éclairé Avatar de MatRem
    Profil pro
    Inscrit en
    Décembre 2002
    Messages
    750
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2002
    Messages : 750
    Points : 693
    Points
    693
    Par défaut Instanciation de fonction template et temporaire
    Bonjour,

    Je me demande pourquoi mes compilateurs (gcc 4.3, et cl de visual c++ 2005) m'instancie cette fonction :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    void f(int & val)
    {
       std::cout << val << std::endl;
    }
    dans ce cas :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    #include <iostream>
     
    template<typename T>
    void f(T & val)
    {
       std::cout << val << std::endl;
    }
     
    int main()
    {
       f(int(0));
    }
    Forcément, avec cette instanciation le code ne peux pas compiler.
    Pourquoi le compilateur n'instancie pas cette fonction quand on lui passe un temporaire ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    void f(int const & val)
    {
       std::cout << val << std::endl;
    }
    Avec ce comportement, mes compilateurs m'obligent à fournir deux versions de fonction template pour gérer le cas du temporaire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    template<typename T>
    void f(T & val)
    {
       std::cout << val << std::endl;
    }
    template<typename T>
    void f(T const & val)
    {
       std::cout << val << std::endl;
    }
    Y'a t il un moyen de factoriser ce genre de code ?

    ps : ma fonction f n'est qu'un exemple ultra simplifié ... et effectivement dans ce cas précis la version const suffirait, mais ma question porte sur le cas général

  2. #2
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Bonjour,
    Je vais répondre de biais :
    1/si tu n'as pas besoin de modifier val, pourquoi déclarer la version non const ? Sachant qu'il existe (dans boost) des moyens de supprimer le qualificateur const s'il est déjà présent dans T.
    2/ Si tu as besoin de modifier val, alors c'est que tu dois distinguer les 2 comportements.

  3. #3
    Membre éclairé Avatar de MatRem
    Profil pro
    Inscrit en
    Décembre 2002
    Messages
    750
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2002
    Messages : 750
    Points : 693
    Points
    693
    Par défaut
    En fait il faut prendre f comme une fonction générique au sens ou elle est indépendante du fait que t soit const ou pas.
    Elle appelle seulement des fonctions sur t, et dans T ses fonctions ont des version const et non const (*).

    Peut être que je peux donner un exemple si mon explication n'est pas assez concrète ...

    (*) : ou pas, et dans ce cas le compilateur pourra effectivement se plaindre de façon justifiée . Par exemple si on passe un temporaire à f et que seules les fonctions non const sont définies dans T.

  4. #4
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Citation Envoyé par MatRem Voir le message
    En fait il faut prendre f comme une fonction générique
    Ma réponse était générique

    Citation Envoyé par MatRem Voir le message
    Elle appelle seulement des fonctions sur t, et dans T ses fonctions ont des version const et non const (*).
    Soit la fonction suppose qu'elle ne modifie pas l'objet, donc elle n'est supposée qu'appeler des fonctions const et alors le paramètre doit être aussi en const.

    Citation Envoyé par MatRem Voir le message
    Peut être que je peux donner un exemple si mon explication n'est pas assez concrète ...
    Je veux bien. Ca éclairera peut être un peu plus le besoin.

  5. #5
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    Que se passe-t-il si tu tentes d'invoquer explicitement f<const int>(0) ?
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  6. #6
    Membre éclairé Avatar de MatRem
    Profil pro
    Inscrit en
    Décembre 2002
    Messages
    750
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2002
    Messages : 750
    Points : 693
    Points
    693
    Par défaut
    Voiilà un exemple qui permet de comprendre le principe.
    Par contre il ne compile pas puisque je ne fournis pas les deux versions de f et que j'utilise un temporaire (c'est fait exprès pour bien voir que dans ce cas je ne devrais pas avoir besoin de fournir les deux) :

    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 T>
    void f(T & t)
    {
       t.call_the_right_function();
    }
     
    struct S
    {
       void call_the_right_function();
       void call_the_right_function() const;
    };
     
    int main()
    {
       f(S()); //Ne compile pas
     
       S const s;
       f(s); //compile trés bien puisque la fonction instanciée est bien void f(T const &)
     
       f<S const>(S()); //fonctionne aussi trés bien
    }

  7. #7
    Membre éclairé Avatar de MatRem
    Profil pro
    Inscrit en
    Décembre 2002
    Messages
    750
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2002
    Messages : 750
    Points : 693
    Points
    693
    Par défaut
    @Médinoc : effectivement cela fonctionne trés bien (cf exemple au dessus).

    J'aimerais comprendre pourquoi le compilateur n'instancie pas la version const par défaut ...

    Le fait de spécifier le type const à l'appel de la fonction est une solution à l'idée de factoriser le code (c'est peut être la seule), mais je la trouve pas trop élégante ...

  8. #8
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    Cette ligne me donne une erreur:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
       f<S const> f(S()); //fonctionne aussi trés bien
    Citation Envoyé par Visual Studio
    error C2146: syntax error : missing ';' before identifier 'f'
    En supprimant le f supplémentaire, ça compile sans erreur ni warning, alors que f(S()) me donne C4239 (conversion from 'S' to 'S &').

    Je me demande si le standard dit quelque chose là-dessus...

    Edit: Ceci compile aussi parfaitement:
    Code C++ : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    const S GetS() { return S(); };
     
    void TestTemplate3()
    {
       f(GetS());
    }
    ...Et foire sans le const.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  9. #9
    Membre éclairé Avatar de MatRem
    Profil pro
    Inscrit en
    Décembre 2002
    Messages
    750
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2002
    Messages : 750
    Points : 693
    Points
    693
    Par défaut
    Citation Envoyé par Médinoc
    Cette ligne me donne une erreur:
    Oui désolé j'ai écrit un peu vite, j'ai édité l'exemple, c'est ça la ligne qui compile :

  10. #10
    Membre éclairé Avatar de MatRem
    Profil pro
    Inscrit en
    Décembre 2002
    Messages
    750
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2002
    Messages : 750
    Points : 693
    Points
    693
    Par défaut
    Une autre solution, que je trouve un peu plus élégante :
    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<typename T>
    inline T const & const_ref(T & ref)
    {
       return ref;
    }
    template<typename T>
    inline T const & const_ref(T const & ref)
    {
       return ref;
    }
     
    int main()
    {
       f(S()); //Ne compile pas
     
       S const s;
       f(s); //compile trés bien puisque la fonction instanciée est bien void f(T const &)
     
       f<S const>(S()); //fonctionne aussi trés bien
     
       f( const_ref( S() ) );
    }
    Par contre je ne sais toujours pas pourquoi les compilateurs (plutôt le standard j'imagine) possède ce comportement.

  11. #11
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Citation Envoyé par MatRem Voir le message
    Une autre solution, que je trouve un peu plus élégante :
    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<typename T>
    inline T const & const_ref(T & ref)
    {
       return ref;
    }
    template<typename T>
    inline T const & const_ref(T const & ref)
    {
       return ref;
    }
     
    int main()
    {
       f(S()); //Ne compile pas
     
       S const s;
       f(s); //compile trés bien puisque la fonction instanciée est bien void f(T const &)
     
       f<S const>(S()); //fonctionne aussi trés bien
     
       f( const_ref( S() ) );
    }
    Par contre je ne sais toujours pas pourquoi les compilateurs (plutôt le standard j'imagine) possède ce comportement.
    Du coup tu passes toujours par un const. Donc à quoi sert la surcharge en non const...

  12. #12
    Membre éclairé Avatar de MatRem
    Profil pro
    Inscrit en
    Décembre 2002
    Messages
    750
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2002
    Messages : 750
    Points : 693
    Points
    693
    Par défaut
    A permettre ça, en faisant en sorte que f appelle la version call_the_right_function non const :

    D'ailleurs avec const_ref, la surcharge n'est plus nécessaire puisque le compilateur instancie tout seul la bonne version de f (T const & ou T &).

    Voilà un exemple complet :
    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
     
    template<typename T>
    void f(T & t)
    {
       t.call_the_right_function();
    }
     
    struct S
    {
       void call_the_right_function();
       void call_the_right_function() const;
    };
     
    template<typename T>
    inline T const & const_ref(T & ref)
    {
       return ref;
    }
    template<typename T>
    inline T const & const_ref(T const & ref)
    {
       return ref;
    }
     
     
    int main()
    {
        f(const_ref( S() )); //call_the_right_function() const
     
        S s;
        f(s); //call_the_right_function() non const
    }

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

    Informations professionnelles :
    Activité : aucun

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

    Je crois qu'il serait temps de reprendre depuis le début en s'intéressant au mot clé const et au concept de "const correctness"...

    Le mot clé const va avoir pour effet de signaler au compilateur que l'on ne peut pas modifier le contenu de la variable (ou de l'argument transmis), avec, comme corrolaire, le fait de rajouter certaines restrictions sur l'usage que l'on peut faire de la variable, comme le fait de ne pouvoir utiliser que les fonctions membres qui se sont engagées à ne pas modifier l'objet (qui sont déclarées const);

    La transformation d'un objet non constant en un objet constant ne posera jamais de problème du fait que l'on rajoute des restrictions d'utilisation, alors que la transformation d'un objet constant en un objet non constant sera refusée car, justement, cela a pour effet de lever certaines restrictions qui peuvent "venir de loin"...

    C'est la raison pour laquelle le principe de "const correctness" conseille largement de préciser systématiquement qu'une variable (ou un argument) n'a pas pour vocation d'être modifié lorsque c'est le cas.

    En effet, une fonction dont la déclaration est proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    template<typename>
    void foo(T const t)
    {
        /* ce qui doit être fait, sans modification du contenu de t */
    }
    sera acceptée qu'elle soit invoquée sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    int i = 3;
    foo<int> (i);
    ou sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    int const j = 5
    foo<int>(j);
    A cela vient se rajouter le problème des références sur objets...

    Pour rappel, une référence est un alias de l'objet référencé, ce qui permet d'éviter une copie lorsqu'un objet est passé en référence ainsi que de s'assurer que toute modification apportée à la référence sera répercutée sur l'objet d'origine.

    C'est ainsi que tu peux en arriver à une classe proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    class MyClass
    {
        public:
            MyClass(std::string const& str):str(str){}
            std::string & getString(){return str;}
            void print() const{std::cout<<str<<std::endl;}
        private:
            std::string str;
    };
    pour laquelle le code suivant sera tout à fait valide
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    int main()
    {
        MyClass c("salut");
        c.print(); // affiche "sallut"
        c.getString() = "le monde";
        c.print(); //affiche "le monde"
        return 0; 
    }
    Si tu veux que l'objet ne puisse pas être modifié, il faut alors prendre deux précautions:
    • signaler que la fonction s'engage à ne pas modifier la classe
    • signaler que la référence renvoyée ne peut pas être modifiée

    Nous pourrions donc tout à fait envisager de modifier la classe en
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class MyClass
    {
        public:
            MyClass(std::string const& str):str(str){}
            // la version non constante
            std::string & getString(){return str;}
            // la version constante
            std::string const & getConstantString() const {return str;}
            void print() const{std::cout<<str<<std::endl;}
        private:
            std::string str;
    };
    ce qui nous mettrait dans la situation suivante:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    int main()
    {
        MyClass noConstant("salut"); // un objet non constant
        MyClass const constant("Le monde"); // et un objet constant
        noConstant.getString(); // accepté, noConstant n'est pas constant
        noConstant.getConstantString(); // accepté, nous rajoutons des
                                        // restictions
        constant.getString(); // refusé... getString n'est pas une fonction
                              // constante
        constant.getConstantString(); // accepté, getConstantString est une
                                     // fonction constante
        return 0;
    }
    En pratique, nous écririons plutôt la classe 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
    12
    class MyClass
    {
        public:
            MyClass(std::string const& str):str(str){}
            // la version non constante
            std::string & getString(){return str;}
            // la version constante
            std::string const & getString() const {return str;}
            void print() const{std::cout<<str<<std::endl;}
        private:
            std::string str;
    };
    et la sélection de la version correcte sera effectuée par le compilateur selon la situation dans laquelel la fonction est invoquée: la version non constante ne sera utilisée que si l'objet au départ duquel la fonction est invoquée n'est pas constant et que la chaine qui sert à la récupération est destinée à être modifiée.

    Il y a enfin un dernier point à prendre en compte lorsque l'on utilise des références comme arguments, c'est le fait que seules les références constantes peuvent être initialisées avec des variables temporaires non nommées.

    Ainsi, si nous avions deux fonctions proches de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    void foo(std::string & str)
    {
        /* n'importe quoi modifiant ou non str */
    }
    void bar(std::string const & str)
    {
        /* n'importe quoi mais sans modification de str */
    }
    nous serions dans la situation suivante:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    int main()
    {
        foo("salut"); // refusé: il est interdit d'utiliser une variable
                      // temporaire non nommée sur une référence non constante
        bar("salut"); // accepté: une variable temporaire non nommée est
                      // acceptée lorsqu'elle est transmise sous la forme d'une
                      // référence constante
        return 0;
    }
    Maintenant que ce (long) rappel a été fait, revenons sur ton problème d'une fonctino template proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    template <typename T>
    vois f(T& t)
    {
    }
    La première chose que tu as à faire, c'est de te poser la question de savoir si f va modifier ou non l'objet qui lui sera passé...

    Tu devrais avoir facilement ta réponse car tu es sensé savoir le but poursuivi par f

    Si ta fonction a pour but de modifier l'objet passé, tu n'aura pas énormément le choix:
    • Soit tu passe l'objet sous la forme d'une référence non constante, ce qui te permet d'utiliser les fonctions membres non constantes de l'objet et de répercuter les modifications apportées dans la variable d'origine
    • Soit tu passe l'objet sous la forme d'une référence constante, dont tu provoques la copie, et tu travailles sur la copie de l'objet (qui te permet d'utiliser les fonctions membres non constantes de l'objet), mais les modifications apportées ne seront pas répercutées sur la variable d'origine.

    Si par contre, ta fonction ne dois pas modifier l'objet passé, il est logique que tu ne puisse utiliser que les fonctions membres qui... se sont engagées à ne pas modifier l'objet

    Si, enfin, tu viens à envisager le fait que les éventuelles modifications soient subordonnées à la constance de l'objet passé en paramètre, il semble cohérent de prévoir deux fonctions clairement différentes (au moins du point de vue du nom de la fonction), car il s'agit de fonctions dont les responsabilités sont clairement différentes
    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

  14. #14
    Membre éclairé Avatar de MatRem
    Profil pro
    Inscrit en
    Décembre 2002
    Messages
    750
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2002
    Messages : 750
    Points : 693
    Points
    693
    Par défaut
    Je vais me répéter, mais j'ai l'impression que mon problème n'est pas clair... (je crois que Médinoc m'a compris )
    Ou alors c'est moi qui n'ait pas compris où tu voulais en venir (a priori c'est juste un petit cours sur l'utilisation de const ? ).

    Il faut prendre f comme une fonction générique au sens ou elle n'est pas consciente de la constance de l'instance t référencée (et elle n'est de toute façon pas concernée par le problème).

    En fait les deux questions soulevées sont :
    - comment éviter d'écrire les deux surcharges de f, quand cela n'est pas nécessaire (et il y a déjà 2 solutions);
    - pourquoi le compilateur instancie la version f(T &) quand on lui passe un temporaire alors qu'il sait très bien que cette version de f n'est pas utilisable avec un temporaire.

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Mais quand tu reprend le raisonnement que tu as suivi pour en arriver à décider de créer une fonction template f, tu remarques qu'il correspond à peu près à:
    1. Je veux factoriser du code qui fournisse tel comportement ==>créons une fonction
    2. Je veux que le code facotrisé fonctionne quel que soit le type de donnée fourni==> rendons la fonction template
    Tu sais dés le départ si le comportement à factoriser va tenter de modifier ou non l'objet qui lui sera fourni en argument, vu que c'est pour factoriser ce comportement que tu as décidé de créer une fonction

    A partir de là, la décision est finalement toute simple: si le comportement ne doit pas modifier l'objet fourni en argument, prend toutes les précaution et déclares cet argument comme constant...

    Cela te donnera l'assurance que toute tentative d'accès à une fonction membre ne s'étant pas engagée à ne pas modifier l'objet sera refusée et que, dans le cas où certaines fonctions membres ont la caractéristique d'avoir une version qui s'est engagée à ne pas modifier l'objet et une autre qui n'a pas pris cet engagement, ce sera la version ayant pris l'engagement qui sera utilisée

    Si, cela peut arriver, ce qu'il te faut c'est une fonction qui a pour but de modifier l'objet et une autre fonction qui a simplement pour but d'agir comme un "visiteur qui ne touche à rien", il te faut deux fonctions clairement différentes indiquant clairement l'objectif qu'elles suivent
    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

  16. #16
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Si, cela peut arriver, ce qu'il te faut c'est une fonction qui a pour but de modifier l'objet et une autre fonction qui a simplement pour but d'agir comme un "visiteur qui ne touche à rien", il te faut deux fonctions clairement différentes indiquant clairement l'objectif qu'elles suivent
    En gros, ce que j'avais essayé d'expliquer au début

  17. #17
    Expert éminent

    Homme Profil pro
    Ingénieur systèmes et réseaux
    Inscrit en
    Février 2007
    Messages
    4 253
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur systèmes et réseaux
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Février 2007
    Messages : 4 253
    Points : 7 618
    Points
    7 618
    Billets dans le blog
    3
    Par défaut
    Je crois que tu te mélange les pinceaux....

    Quand tu appelles:
    Le compilateur va chercher les fonctions "f" disponibles, avec comme parametre 'int' ou 'int&'... S'il en trouve plusieurs... il va râler...
    S'il n'en trouve pas... il va essayer avec un const, genre: 'const int' ou 'const int&'...
    S'il n'en trouve toujours pas... il va essayer avec une promotion de type.
    Et à chaque cas, si il y a plusieurs choix, il va râler en demandant d'être plus explicite...

    Dans ton cas... il trouve immédiatement une fonction qui match avec T=int:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    template<typename T>
    void f(T & val)
    {
       std::cout << val << std::endl;
    }
    T = int
    Donc il va s'arrêter là et utiliser (en remplacant T par int):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    template<int>
    void f(int& val)
    Puis il va râler en disant: hey ! moi je veux bien utiliser cette fonction, c'est la seule qui correspond d'ailleurs, mais je peux pas convertir l'objet temporaire int(0) en int& ! Ce sur quoi il a raison...


    Maintenant, le problême du const... Tu as raison en disant:
    elle n'est pas consciente de la constance de l'instance t référencée
    car c'est le compilateur qui en a conscience... Mais le raisonnement est faux: comme dit koala (grilled !) c'est la fonction appellée qui a conscience du 'constness' de ses paramètres (bref de ce qu'elle va en faire)... et ainsi, indiquer au compilateur quelle(s) fonction(s) il peut sélectionner...

    En déclarant le template ci-dessus tu dis: J'ai une fonction f<T>() qui *va* modifier le parametre passé (voir même prendre une référence dessus) !
    Et le compilateur te dis: ok moi je veux bien utiliser cette fonction mais c'est pas raisonnable avec une variable temporaire !
    N'oubliez pas de cliquer sur mais aussi sur si un commentaire vous a été utile !
    Et surtout

  18. #18
    Expert éminent

    Homme Profil pro
    Ingénieur systèmes et réseaux
    Inscrit en
    Février 2007
    Messages
    4 253
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur systèmes et réseaux
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Février 2007
    Messages : 4 253
    Points : 7 618
    Points
    7 618
    Billets dans le blog
    3
    Par défaut
    Juste une addition par rapport à l'explication de koala:
    dans le cas où certaines fonctions membres ont la caractéristique d'avoir une version qui s'est engagée à ne pas modifier l'objet et une autre qui n'a pas pris cet engagement, ce sera la version ayant pris l'engagement qui sera utilisée
    C'est pas tout à fait vrai... Le compilateur est assez bête...

    void f(const int& v);
    void f(int& v);

    int a;
    f(a) => va appeler f(int& v);
    f(2) => va appeler f(const int& v);


    Dans le cadre d'une fonction membre:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    class A
    {
        void f(const int& v) const;
        void f(int& v);
    }
    se traduit en:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    void A__f(const A* _this, const int& v);
    void A__f(A* _this, int& v);
    Ce qui revient au résultat:
    A constant, v constant => f(const)const
    A constant, v non constant => f(const)const
    A non constant, v constant => f(const)const
    A non constant, v non constant => f()
    N'oubliez pas de cliquer sur mais aussi sur si un commentaire vous a été utile !
    Et surtout

  19. #19
    Membre éclairé Avatar de MatRem
    Profil pro
    Inscrit en
    Décembre 2002
    Messages
    750
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2002
    Messages : 750
    Points : 693
    Points
    693
    Par défaut
    Citation Envoyé par koala01
    Tu sais dés le départ si le comportement à factoriser va tenter de modifier ou non l'objet qui lui sera fourni en argument, vu que c'est pour factoriser ce comportement que tu as décidé de créer une fonction
    Et non justement ma fonction est encore plus générique, elle est aussi indépendant de la constance de son paramètre.

    Mais de toute façon je ne me posais pas de question au niveau de la conception... même si c'est intéressant d'en discuter

  20. #20
    Membre éclairé Avatar de MatRem
    Profil pro
    Inscrit en
    Décembre 2002
    Messages
    750
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2002
    Messages : 750
    Points : 693
    Points
    693
    Par défaut
    Citation Envoyé par nicroman
    En déclarant le template ci-dessus tu dis: J'ai une fonction f<T>() qui *va* modifier le parametre passé (voir même prendre une référence dessus) !
    Et le compilateur te dis: ok moi je veux bien utiliser cette fonction mais c'est pas raisonnable avec une variable temporaire !
    Tout à fait f<T> peut potentiellement appeller des fonctions de t qui n'ont pas de version const et dans ce cas je suis tout à fait d'accord que le compilateur râle.
    Mais dans mon exemple on peut voir que f appelle des fonctions sur t qui dont les deux versions const et non const existe. Le code de f est donc tout à fait indépendant de la constance de t (encore une fois ).

    Citation Envoyé par nicorman
    Donc il va s'arrêter là et utiliser (en remplacant T par int):
    Et la question est : "pourquoi ?"

    Pourquoi avec un temporaire essaye t il d'instancier cette fonction et non pas f(int const &), ça lui éviterai de râler .

Discussions similaires

  1. Réponses: 2
    Dernier message: 02/10/2008, 16h37
  2. Fonction template virtuelle... comment l'éviter ?
    Par :Bronsky: dans le forum Langage
    Réponses: 12
    Dernier message: 07/06/2005, 14h21
  3. fonctions template
    Par romeo9423 dans le forum Langage
    Réponses: 12
    Dernier message: 22/01/2005, 16h13
  4. Fonctions et tables temporaires ?!
    Par devdev dans le forum MS SQL Server
    Réponses: 2
    Dernier message: 16/12/2004, 11h46
  5. Fonctions template+friend sous VC7
    Par patapetz dans le forum MFC
    Réponses: 12
    Dernier message: 24/09/2004, 11h16

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