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. #21
    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
    Citation Envoyé par nicroman Voir le message
    Maintenant, le problême du const... Tu as raison en disant:
    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 !
    C'est surtout que, lorsque tu invoque une fonction sous la forme de
    la valeur 1 est transformée en variable temporaire non nommée (temporaire car elle n'existe que pour l'appel de f et non nommée car tu ne dispose nulle part ailleurs d'un identifiant permettant de récupérer la valeur).

    Or, la norme est particulièrement claire là dessus: lorsqu'il s'agit de travailler avec des références, seules les références constantes peuvent être initialisée avec des variables temporaires non nommées...

    Comme tu ne respectes pas les règles, le compilateur s'en plaint violemment

    Les possibilités dont on dispose sont
    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
    /* argument passé par valeur: il y a création d'une variable nommée t
     * qui est la copie de la variable passée en paramètre
     */
    template< typename T>
    void f( T t)
    {
    }
    /* arguement passé par valeur constante: il y a création d'une variable
     * (rendue consttante pour l'occasion) nommée t
     * qui est la copie de la variable passée en paramètre
     */
    template <typename T>
    void g(T const t)
    {
    }
    /* argument passé par référence constante: il n'y aura création
     * d'une variable temporaire non nommée que si le paramètre
     * passé n'est pas une variable du type adéquat à la base
     * t est un alias de la variable passée en paramètre (éventuellement
     * de la variable temporaire non nommée créée spécialement pour l'occasion)
     */
    template <typename T>
    void h(T const & t)
    {
    }
    /* ne fonctionnera que lorsqu'on invoque la fonction en lui passant
     * une variable existante par ailleurs
     * t est un alias de la variable passée en paramètre
     */
    template <typename T>
    void i(T & t)
    {
    }
    et nous place face aux situations suivantes:
    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
    int main()
    {
        f<int> (2); // ok, création d'une variable nommée t dans f
        int i = 3;
        f<int>(i); // ok: création d'uen varaible nommée t dans f qui est la 
                   // copie de i
        g<int>(2); // ok: création d'une variable (constante) nommée t dans g
        g<int>(i); // idem
        h<int>(2); // ok: création d'une variable temporaire non nommée
                   // qui sera passée par référence constante sous le nom de t
        h<int>(i); // ok: passage d'une variable existante sous la forme
                   // d'une référence constante
        i<int>(2); // refusé: seule les références constantes peuvent accepter
                   // les variables temporaires non nommées
        i<int>(i); // par contre, on peut passer n'importe quelle variable
                   // existante sous la forme d'une référence
    }
    Mais pour en revenir au choix de passer l'arguement sous la forme d'une référence constante ou non constante...

    Lorsque tu décide d'écrire ta fonction, la logique voudrait que tu aie déjà réfléchi à l'algorithme que tu va mettre en oeuvre au sein de cette fonction:

    Tu *devrais* en effet avoir une idée aussi précise que possible des différentes étapes qui seront suivies par ta fonction afin de lui permettre de présenter le comportement souhaité...

    Tu *devrais* donc normalement être en mesure de déterminer si l'objet qui sera passé en paramètre sera modifié ou non

    Or nous savons que nous ne risquons rien à faire passer "temporairement" (comprend: le temps que dure la fonction appelée) un objet qui n'est pas constant à la base pour un objet constant: tout ce que nous faisons, c'est restreindre les différentes interactions que nous pouvons envisager sur cet objet

    D'un autre coté, nous savons aussi que, si nous décidons de permettre la modification de l'objet, il faut que notre objet soit à la base déclaré comme non constant: si l'objet est déclaré à la base constant, nous n'en attendons pas moins de la part du compilateur que le fait de nous signaler que nous allons tenter de modifier un objet qui ne peut normalement pas l'être

    C'est la raison pour laquelle le principe de const correctness t'incite, chaque fois qu'une fonction ne doit pas modifier un objet passé en argument, à indiquer à cette fonction de considérer "temporairement" (comprend: le temps que dure l'exécution de la fonction) l'argument passé "comme s'il était destiné à ne pas être modifié"...
    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. #22
    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 koala1
    Tu *devrais* donc normalement être en mesure de déterminer si l'objet qui sera passé en paramètre sera modifié ou non
    Pkoi je ne peux pas avoir une fonction indépendante de la constance de t (personnellement ça me sert vraiment ce genre de concept).
    Si vraiment cela te dérange disons que c'est une hypothèse de travail "f est une fonction indépendante de la constance de t"

    Citation Envoyé par Koala1
    Or, la norme est particulièrement claire là dessus: lorsqu'il s'agit de travailler avec des références, seules les références constantes peuvent être initialisée avec des variables temporaires non nommées...
    Oui c'est tout à fait juste, et donc pourquoi (ce n'est pas moi qui essaye d'instancier la fonction template f(T &)) le compilateur "ne respecte pas la règle" et essaye d'instancier cette fonction qui selon le standard ne peut pas prendre un temporaire?

    ps : dans ton exemple tu forces T = int donc tu ne laisses pas grand choix au compilateur.

  3. #23
    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
    Citation Envoyé par MatRem Voir le message
    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
    MAis lorsque tu décide qu'une fonction doit présenter un comportement, c'est que tu as déjà déterminé le comportement à implémenter... sinon, tu n'a aucune raison d'écrire une fonction qui ne fait rien
    Citation Envoyé par MatRem Voir le message
    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 .
    Parce que le "cv-qualifier" fait partie de la définition de type...

    Lorque tu indique que la forme canonique d'une déclaration de variable va prendre la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    <type_identifier> nom_variable;
    <type_identifier> correspond à:
    • type_name (int, char, MaClass,...) +
    • cv_qualifier (const, volatile... defaut: volatile) +
    • memory_access( (rien), *, &, *&, défaut: rien)

    ce qui fait que le type int est différent du type int const...

    Lorsque tu généralilse une fonction sous la forme de
    template< typename T>
    tu indiques au compilateur que l'objet de type T doit etre non constant.

    Or la "conversion" d'un objet constant en un objet non constant doit être explicite (par utilisation de const_cast ou par création d'une copie de l'objet).

    Par contre, lorsque tu généralise une autre fonction sous la forme de
    template<typename T>
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    void bar( T const /* &  */ t);
    tu dis explicitement que t doit être constant, et la conversion d'un objet à la base non constant en un objet constant peut se faire de manière implicite (sans que tu ne précise qu'il faille effectuer la conversion)...

    C'est pour cela que, si tu déclares plutôt foo que bar, le compilateur, en "exécutant bête et méchant" ne "pensera pas" à implémenter une version constante de la fonction
    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. #24
    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 koala1
    tu indiques au compilateur que l'objet de type T doit etre non constant.
    alors pourquoi dans ce cas le compilateur génère la version f(T const &) :

    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 & t)
    {
    }
     
    int main()
    {
    int const i;
    f(i);
    }
    ps : dans ce cas T = int const

  5. #25
    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
    Citation Envoyé par MatRem Voir le message
    Pkoi je ne peux pas avoir une fonction indépendante de la constance de t (personnellement ça me sert vraiment ce genre de concept).
    Si vraiment cela te dérange disons que c'est une hypothèse de travail "f est une fonction indépendante de la constance de t"
    Mais quand, dans ton EDI, tu va ouvrir un fichier (hpp) et commencer à écrire
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    template <typename T>
    void foo( T /* & */ t)
    {
       //ici, tu sais le comportement que tu dois implémenter pour ta fonction
    }
    tu sais ce que la fonction va devoir faire, et si elle va devoir modifier t ou non...

    Autrement, tu ne commencerais même pas à implémenter la fonction en question...

    Ou, du moins, tu devrais avoir une idée suffisamment claire de ce qui sera fait pour pouvoir déterminer s'il y aura modification ou non de l'objet passé en paramètre

    Dés lors, pourquoi ne pas respecter le principe simple qui consiste à passer un objet constant s'il ne doit pas être modifié cela te facilitera la vie lors de l'utilisation de la fonction
    Oui c'est tout à fait juste, et donc pourquoi (ce n'est pas moi qui essaye d'instancier la fonction template f(T &)) le compilateur "ne respecte pas la règle" et essaye d'instancier cette fonction qui selon le standard ne peut pas prendre un temporaire?

    ps : dans ton exemple tu forces T = int donc tu ne laisses pas grand choix au compilateur.
    Les réponses se sont croisées, mais tout vient du fait que le "cv-qualifier" fait partie de "l'identificateur de type", et donc qu'un objet de type non constant est différent d'un objet de type identique mais constant (même si on peut convertir implicitement un objet non constant en objet constant)

    Ceci dit, que tu forces T = int ou non, le problème reste le même (par contre, tu as peut etre justement l'avantage de clairement indiquer le type qui doit être utilisé )
    Citation Envoyé par MatRem Voir le message
    alors pourquoi dans ce cas le compilateur génère la version f(T const &) :

    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 & t)
    {
    }
     
    int main()
    {
    int const i;
    f(i);
    }
    ps : dans ce cas T = int const
    Parce que le compilateur est par nature paresseux et va prendre la première version qui lui semble valide...

    Dans ton exemple, tu indiques explicitement que i est de type int const... et il va donc "logiquement" utiliser ce type particulier.

    Par contre, si tu appelles f sous la forme de f(153); le type le plus simple permettant d'instancier l'appel sera, en dehors de toute indication de ta part, le type... int et non le type int const

    Ce sera donc vers le type int que le compilateur se tournera naturellement... Et c'est là que les problèmes commenceront...

    En effet, après avoir implémenté la version prenant un int &, du fait même de son mode de fonctionnement, il ne fera plus marche arrière (pour l'appel envisagé) et il se rendra compte que cela revient à lui demander de créer une variable temporaire non nommée... ce qu'il ne peut pas faire si c'est pour l'utiliser sous la forme d'une référence non constante...
    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. #26
    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 Koala1
    tu sais ce que la fonction va devoir faire, et si elle va devoir modifier t ou non...
    Voilà un exemple un peu plus "concret" pour essayer de te faire partager mon intérêt pour le concept de fonction non concernée par la constance de leur paramètre :

    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
    template<typename Q, typename F, typename P>
    typename P::Type ForEachChild(Q & quadtree, F & functer, P & policy)
    {
    	assert(!quadtree.IsLeaf());
     
    	for (size_t i = 0; policy.Continue() && i < MAX_CHILD; ++i)
    	{
    		policy.Call(
    			tools::const_ref( boost::bind<typename P::Type>(
    				boost::ref(functer),
    				boost::ref(quadtree.GetChild(Index(i)))
    			) )
    		);
    	}
     
    	return policy.Result();
    }
    Cette exemple ne peut pas fonctionner avec un functer temporaire (entre autre), hors je ne sais pas du tout dans cette fonction si je vais devoir modifier mon functer.
    En fait c'est la policy qui dans Call appellera l'opérateur parenthèse du functer.

    C'est à l'utilisateur de la fonction ForEachChild de gérer la constance => si l'utilisateur passe un functer const, il doit définir un opérateur parenthèse const dans son functer.


    Citation Envoyé par Koala1
    Par contre, si tu appelles f sous la forme de f(153); le type le plus simple permettant d'instancier l'appel sera, en dehors de toute indication de ta part, le type... int et non le type int const
    Quel est l'intérêt pour le compilateur d'avoir ce comportement,
    Puisque'à la compilation il sait :
    - qu'il a affaire a un temporaire;
    - que les temporaire ne peuvent passer par référence que vers un type const;

  7. #27
    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
    Voilà un exemple un peu plus "concret" pour essayer de te faire partager mon intérêt pour le concept de fonction non concernée par la constance de leur paramètre :

    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
    template<typename Q, typename F, typename P>
    typename P::Type ForEachChild(Q & quadtree, F & functer, P & policy)
    {
    	assert(!quadtree.IsLeaf());
     
    	for (size_t i = 0; policy.Continue() && i < MAX_CHILD; ++i)
    	{
    		policy.Call(
    			tools::const_ref( boost::bind<typename P::Type>(
    				boost::ref(functer),
    				boost::ref(quadtree.GetChild(Index(i)))
    			) )
    		);
    	}
     
    	return policy.Result();
    }
    Cette exemple ne peut pas fonctionner avec un functer temporaire (entre autre), hors je ne sais pas du tout dans cette fonction si je vais devoir modifier mon functer.
    En fait c'est la policy qui dans Call appellera l'opérateur parenthèse du functer.
    Ben, en général le functeur est passé par valeur et il est constant. Ce sont éventuellement les paramètres qu'ils prend qui seront l'objet de la modification.
    Je continue de partager l'avis de Koala : si tu dois avoir une version non const, alors il faut la différencier de la version const.

  8. #28
    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
    Citation Envoyé par MatRem Voir le message
    Voilà un exemple un peu plus "concret" pour essayer de te faire partager mon intérêt pour le concept de fonction non concernée par la constance de leur paramètre :

    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
    template<typename Q, typename F, typename P>
    typename P::Type ForEachChild(Q & quadtree, F & functer, P & policy)
    {
    	assert(!quadtree.IsLeaf());
     
    	for (size_t i = 0; policy.Continue() && i < MAX_CHILD; ++i)
    	{
    		policy.Call(
    			tools::const_ref( boost::bind<typename P::Type>(
    				boost::ref(functer),
    				boost::ref(quadtree.GetChild(Index(i)))
    			) )
    		);
    	}
     
    	return policy.Result();
    }
    Cette exemple ne peut pas fonctionner avec un functer temporaire (entre autre), hors je ne sais pas du tout dans cette fonction si je vais devoir modifier mon functer.
    En fait c'est la policy qui dans Call appellera l'opérateur parenthèse du functer.

    C'est à l'utilisateur de la fonction ForEachChild de gérer la constance => si l'utilisateur passe un functer const, il doit définir un opérateur parenthèse const dans son functer.
    Je vois mieux quel est ton problème...
    Ton foncteur ne devra pas être modifé

    Au mieux, le foncteur doit modifier ou non l'objet qui lui est passé en paramètre

    Mais cela implique que c'est ta politique qui doit permettre de "mettre en évidence" si ton foncteur modifiera ou non l'objet passé en paramètre

    C'est à dire qu'il faut veiller à fournir une spécialisation de ta politique de manière à ce qu'elle considère les objets constants différemment des objets non constant

    Typiquement, la solution est donnée par les différentes implémentations des (policy / call) traits que l'on trouve dans boost et / ou dans la STL:
    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
     
    /* la définition "de base" (non constante, non référence, ni rien) */
    template <typename T>
    struct call_traits
    {
    public:
       typedef T value_type;
       typedef T& reference;
       typedef const T& const_reference;
       /* simplifé à l'extrème ;) */
       typedef T param_type;
    };
    /* spécialisation pour les références */
    template <typename T>
    struct call_traits<T&>
    {
       typedef T& value_type;
       typedef T& reference;
       typedef const T& const_reference;
       typedef T& param_type;
    };
    /* spécialisation pour les références constantes */
    template <typename T>
    struct call_traits<T&const>
    {
       typedef T& value_type;
       typedef T& reference;
       typedef const T& const_reference;
       typedef T const & param_type;
    };
    /* specialisation pour les références explicitement volatiles */
    template <typename T>
    struct call_traits<T&volatile>
    {
       typedef T& value_type;
       typedef T& reference;
       typedef const T& const_reference;
       typedef T& param_type;  
    };
    /* spécialisation pour les référence volatiles constantes (si si, ca arrive :D)*/
    template <typename T>
    struct call_traits<T&const volatile>
    {
       typedef T& value_type;
       typedef T& reference;
       typedef const T& const_reference;
       typedef T& param_type;  
    };
    /* spécialistation pour les pointeurs */
    template <typename T>
    struct call_traits< T * >
    {
       typedef T * value_type;
       typedef T * & reference;
       typedef T * const param_type;
    };
    et tu spécialise ta fonction sur base de cette politique
    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
    /* la fonction "de base" */
    template<typename Q, typename F, typename P>
    typename P::Type ForEachChild(Q & quadtree, F & functer, P & policy)
    {
    	assert(!quadtree.IsLeaf());
     
    	for (size_t i = 0; policy.Continue() && i < MAX_CHILD; ++i)
    	{
    		policy.Call(
    			tools::const_ref( boost::bind<typename P::Type>(
    				boost::ref(functer),
    				boost::ref(quadtree.GetChild(Index(i)))
    			) )
    		);
    	}
     
    	return policy.Result();
    }
    /* et les spécialisations */
    template<typename Q, typename F>
    typename P<Q, F, call_traits< const T> >::Type 
    ForEachChild<Q, F, call_traits< const T> >(Q & quadtree, F & functer, 
                                               call_traits<const T> & P){
    /* à toi de jouer :D */
    }
    Quel est l'intérêt pour le compilateur d'avoir ce comportement,
    Puisque'à la compilation il sait :
    - qu'il a affaire a un temporaire;
    - que les temporaire ne peuvent passer par référence que vers un type const;
    Je n'ai pas dit que cela avait le moindre intérêt (car cela n'en a strictement aucun), j'ai dit que, du fait de son mode de fonctionnement et de sa "paresse naturelle", c'est malheureusement comme cela qu'il fonctionne...

    Je ne suis pas assez doué dans le fonctionnement même des compilateur pour t'en donner la raison précise, mais cette "incohérence" vient principalement du fait qu'ils fonctionnent en utilisant principalement des piles d'implémentations qui font qu'ils "perdent" en cours de route le fait qu'il devront manipuler une variable temporaire (je crois d'ailleurs que cela a aussi à voir avec le fait qu'une seule conversion implicite est autorisée par la norme alors que l'appel sous la forme de f(152) en nécessiterait deux ou trois: de 152-->int, de int--> int const et de int const--> int const & )
    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

  9. #29
    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 Koala1
    Ton foncteur ne devra pas être modifé
    Pourquoi pas? En tout cas cette fonction ne modifie pas explicitement l'état du foncteur, elle permet juste à l'utilisateur de passer un foncteur non const. Et le cas échéant suivant la composition Quadtree/Functer/Policy ne pouvoir le modifier, soit parceque son opérateur parenthèse n'est pas const (parce qu'il modifie l'état interne de l'instance), soit parceque la policy modifie le functer.
    C'est juste un niveau de liberté supplémentaire pour l'utilisateur de l'API.

    Si j'avais voulu ne pas le permettre j'aurais : F const &.

    D'ailleurs j'ai permis la même liberté pour le quadtree et la policy.

    Citation Envoyé par Koala1
    /* à toi de jouer */
    Que ferait de plus ou de moins cette spécialisation ?...

    Citation Envoyé par Koala1
    ils fonctionnent en utilisant principalement des piles d'implémentations qui font qu'ils "perdent" en cours de route le fait qu'il devront manipuler une variable temporaire
    Intéressant... donc a priori se serait lié à un problème technique au niveau du parseur?

    J'aimerais bien approfondir ce point là, si quelqu'un peut m'en dire plus.

  10. #30
    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
    Citation Envoyé par MatRem Voir le message
    Intéressant... donc a priori se serait lié à un problème technique au niveau du parseur?

    J'aimerais bien approfondir ce point là, si quelqu'un peut m'en dire plus.
    Ne pars pas trop vite en guerre sur base de mes écrits

    Un problème technique au niveau du parser est une solution, mais une autre, bien plus vraisemblable, vient d'une restriction imposée par la norme quant aux différentes conversions et la manière dont elles sont envisageables

    N'oublie pas non plus le fait de transmettre une variable temporaire non nommée est l'exception par rapport à la règle générale d'appel d'une fonction: nous transmettons généralement une variable existant par ailleurs

    La règle selon laquelle une variable temporaire non nommée ne peut être utilisée que sur un objet constant (quand il s'agit de travailler avec une référence) vient de la constatation logique selon laquelle il est inutile (et dangeureux ) de permettre la modification d'une telle variable temporaire: les modifications apportées (et répercutées) sur la variable disparaissant "logiquement" en même temps que la variable, c'est à dire... lors de la sortie de la portée de la fonction appelée...

    Il semble "logique" que, entre une règle générale et une exception, le compilateur fasse d'abord attention à la règle générale (toute variable transmise en argument existe par ailleurs), plutôt qu'à l'exception (mais, parfois, on peut utiliser une variable temporaire non nommée)
    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

  11. #31
    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
    Citation Envoyé par MatRem Voir le message
    Pourquoi pas? En tout cas cette fonction ne modifie pas explicitement l'état du foncteur, elle permet juste à l'utilisateur de passer un foncteur non const. Et le cas échéant suivant la composition Quadtree/Functer/Policy ne pouvoir le modifier, soit parceque son opérateur parenthèse n'est pas const (parce qu'il modifie l'état interne de l'instance), soit parceque la policy modifie le functer.
    C'est juste un niveau de liberté supplémentaire pour l'utilisateur de l'API.

    Si j'avais voulu ne pas le permettre j'aurais : F const &.

    D'ailleurs j'ai permis la même liberté pour le quadtree et la policy.


    Que ferait de plus ou de moins cette spécialisation ?...
    La spécialisation agirait principalement sur le type de retour envisagé, selon quatre axes principaux (en excluant l'axe des pointeurs):
    • Le type lui-même (call_traits::value_type) pour les foncteurs non constants renvoyant une instance
    • le type lui-même mais déclaré constant (call_traits::value_type const) pour les foncteurs constants renvoyant une instance (constante)
    • une référence non constante (call_traits::reference) pour les foncteurs non constants renvoyant une référence non constante
    • une référence constante (call_traits::const_reference) pour les foncteurs constant renvoyant une... référence constante


    Cela se traduirait principalement par le fait que le retour de fonction est susceptible de changer selon le cas, et par le fait d'utiliser, dans certains cas boost::cref au lieu de boost::ref
    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

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