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 :

Conversions implicites - Comment forcer la bonne ?


Sujet :

Langage C++

  1. #1
    Membre averti
    Profil pro
    Inscrit en
    Août 2007
    Messages
    32
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2007
    Messages : 32
    Par défaut Conversions implicites - Comment forcer la bonne ?
    Bonjour,

    J'ai actuellement un problème en utilisant des pointeurs intelligents. En effet, ces pointeurs ont deux versions des opérateurs de déréférencement, une version constante, et une version "normale" (non constante, donc).

    Le souci est que, si on utilise la version non-constante, un traîtement parasite (copie du contenu) est réalisé, alors que la version constante du déréférencement permet de garder une unique instance, partagée entre les différents pointeurs intelligents.

    En gros, je cherche à pouvoir forcer de manière "simple" l'utilisation du déréférencement constant, quand c'est possible. Actuellement, avec un pointeur intelligent P<X> p, et une méthode X:: x() const (pouvant travailler sur un objet constant, donc), le fait d'appeler p->x() utilise le déréférencement non-constant, ce qui est gênant.


    Plus de détails sur le forum Qt (question initialement posée là-bas, mais il semblerait que le souci soit plutôt dû à une mauvaise compréhension des règles C++, de ma part, d'où ce sujet ici.)

    http://www.developpez.net/forums/d95...re-incorrecte/



    Merci par avance.

  2. #2
    Expert confirmé
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Février 2005
    Messages
    5 474
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : France, Val de Marne (Île de France)

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

    Informations forums :
    Inscription : Février 2005
    Messages : 5 474
    Par défaut
    Avec un simple const_cast, non ?

  3. #3
    Membre averti
    Profil pro
    Inscrit en
    Août 2007
    Messages
    32
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2007
    Messages : 32
    Par défaut
    Euh... j'avais cru comprendre que le "const_cast" était là pour permettre de virer le modificateur const, non ? Si c'est le cas, ce n'est pas ce que je veux.


    En gros, la classe A a deux opérateurs, T* operator ->() const, et const T* operator ->() const.

    Je ne sais pas comment "forcer" l'un plutôt que l'autre, de manière agréable, sachant que si on écrit A->methode_const_de_T(), il a une fâcheuse tendance à utiliser le déréférencement non-constant quand même.



    Parce que, bon, je peux toujours faire
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    const T *t = a.operator const T*();
    t->methode_const_de_T();
    mais je perds alors une bonne partie de l'intérêt du pointeur intelligent, à savoir s'utiliser comme un pointeur, sans réfléchir.

  4. #4
    Expert confirmé
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Février 2005
    Messages
    5 474
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : France, Val de Marne (Île de France)

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

    Informations forums :
    Inscription : Février 2005
    Messages : 5 474
    Par défaut
    Euh... j'avais cru comprendre que le "const_cast" était là pour permettre de virer le modificateur const, non ? Si c'est le cas, ce n'est pas ce que je veux.
    Ca marche dans les deux sens.

  5. #5
    Membre averti
    Profil pro
    Inscrit en
    Août 2007
    Messages
    32
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2007
    Messages : 32
    Par défaut
    Bon à savoir, que ça marche dans les deux sens.

    Cependant, c'est pas tellement d'avoir un const *T plutôt qu'un *T, qui m'intéresse. Ce que je voudrais réellement, c'est utiliser la méthode retournant un const *T plutôt que la méthode retournant un *T.

    La raison est simple : la version non-constante ajoute une opération (un Copy On Write, via appel d'une méthode detach() ), que je ne veux surtout pas voir executée. Donc obtenir le pointeur non-constant, puis le rendre constant via un cast, n'est vraiment pas le comportement souhaité.


    N'y-a-t'il pas de moyen pour inciter le compilateur C++ à utiliser la méthode retournant le type qui correspond exactement, plutôt qu'un type approchant qui est ensuite casté pour rentrer ?

  6. #6
    Membre Expert
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Par défaut
    Si ton pointeur intelligent est lui-même const, ça devrait appeler la bonne méthode, non ?

    En gros, la classe A a deux opérateurs, T* operator ->() const, et const T* operator ->() const
    Il y a un const de trop sur le premier à mon avis, ça ne peut pas marcher ça. Hop, direction la doc Qt, c'est bien ça, c'est :

    T* operator ->()
    const T* operator ->() const

    donc si tu as un const QSharedDataPointer, il appellera nécessairement la méthode const. Après, je ne sais pas si ça te pose un problème ailleurs qu'il soit const.

  7. #7
    Membre averti
    Profil pro
    Inscrit en
    Août 2007
    Messages
    32
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2007
    Messages : 32
    Par défaut
    Effectivement, c'est une piste. Mais, oui, ça pose un souci qu'il soit const.

    En gros, le comportement souhaité, c'est qu'avec un même pointeur (non-const, donc), on puisse appeler des méthodes const (sans qu'il lance le COW), et des méthodes non const (auquel cas, il lancera le COW). Sauf que les modifications n'ont rien d'inéluctable.

    Par exemple, prendre une liste de pointeurs intelligents "normaux", pointant vers des objets qu'on va afficher. Pour tous ces objets, on va utiliser les accesseurs de lecture, pour les données nous intéressant. On a pas vraiment envie de tous les copier.

    Puis, un des objets affichés sera peut-être modifié. D'où l'intérêt d'avoir une liste de pointeurs non const. Sinon, on peut passer une liste "const", puis faire un const_cast sur le pointeur de l'objet à modifier, mais ça me parait déja assez crade (et éloigné des fondamentaux), et aussi totalement contraire à la logique du programme.

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

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

    Informations forums :
    Inscription : Février 2005
    Messages : 5 474
    Par défaut
    Franchement, avoir deux méthodes avec le même nom dans une même classe, qui ne font pas la même chose, c'est largement plus crade.
    La sémantique de la fonction est const ou pas, si oui, vous passez des consts, quitte à utiliser des "mutables", si non, vous passez des non_consts à la méthode.
    Pensez à la sémantique de la méthode et à la lisibilité du code.

  9. #9
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Yvelines (Île de France)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Par défaut
    Je ne sais pas si tu as bien compris comment se résolvais la surcharge sur const. Elle ne peut pas se résoudre en fonction de la manière dont on va utiliser la valeur de retour, mais uniquement en fonction de si l'objet sur lequel on appelle la fonction est constant ou pas.
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  10. #10
    Membre Expert
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Par défaut
    Citation Envoyé par kzwix Voir le message
    Effectivement, c'est une piste. Mais, oui, ça pose un souci qu'il soit const.

    En gros, le comportement souhaité, c'est qu'avec un même pointeur (non-const, donc), on puisse appeler des méthodes const (sans qu'il lance le COW), et des méthodes non const (auquel cas, il lancera le COW). Sauf que les modifications n'ont rien d'inéluctable.

    Par exemple, prendre une liste de pointeurs intelligents "normaux", pointant vers des objets qu'on va afficher. Pour tous ces objets, on va utiliser les accesseurs de lecture, pour les données nous intéressant. On a pas vraiment envie de tous les copier.

    Puis, un des objets affichés sera peut-être modifié. D'où l'intérêt d'avoir une liste de pointeurs non const. Sinon, on peut passer une liste "const", puis faire un const_cast sur le pointeur de l'objet à modifier, mais ça me parait déja assez crade (et éloigné des fondamentaux), et aussi totalement contraire à la logique du programme.
    Je pense qu'il te faut un pointeur intrusif pour faire ce genre de chose. Le compilateur ne fait pas de surcharge en fonction du type de la valeur de retour, donc tel quel ça ne pourra simplement pas marcher.

    Soit tu const ton pointeur avant d'appeler des méthodes const, soit c'est la version non-const de -> qui est appelée, et donc tu te prends le COW.

    Soit dit en passant, je trouve bizarre la sémantique de ce pointeur. En général, quand on fait un smart_ptr, on s'arrange pour que la sémantique de const smart_ptr<char> corresponde à celle de char * const. Ici, ça ne me semble pas le cas.

  11. #11
    Membre averti
    Profil pro
    Inscrit en
    Août 2007
    Messages
    32
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2007
    Messages : 32
    Par défaut
    Aha, nous avons un gagnant !

    Merci à tous pour les réponses, mais tout spécialement à JolyLoic, pour son explication. Je pense, en effet, que ça doit être la cause du problème, et que c'était l'info qui me manquait.

    Cela dit, je pense coder une classe-interface qui agira comme un pointeur vraiment intelligent, et sera capable de décider, en fonction des méthodes appelées, et éventuellement des paramètres de ces méthodes, si il y a lieu de lancer une COW.


    Merci !

    PS : @bacelar, ce n'est pas nous qui avons codé ça, c'est une classe de Qt, à savoir QSharedDataPointer. Mais la sémantique me semble normale, dans le sens où ils tentent de s'approcher, autant que possible, d'un pointeur normal.

  12. #12
    Membre averti
    Profil pro
    Inscrit en
    Août 2007
    Messages
    32
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2007
    Messages : 32
    Par défaut
    Je continue, par contre, avec une question subsidiaire (oui, d'où ce double post, vu que ça n'a plus grand rapport avec ce qui précède).

    Quand je cherchais à voir ce qui appellerait l'un plutôt que l'autre, j'ai écrit ce bout de 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
    QSharedDataPointer<TestSharedData> pj(new TestSharedData(a, QString("G")));
    std::cout << "pj->x() = ##" << pj->x() << "##\n";
    QSharedDataPointer<TestSharedData> pk = pg;
    QSharedDataPointer<TestSharedData> pl = pg;
    std::cout << "--\n";
    const TestSharedData *ppj = (TestSharedData *) pj;
    std::cout << "--\n";
    const TestSharedData *ppk = (const TestSharedData *) pk;
    std::cout << "--\n";
    const TestSharedData *ppl = pl;
    std::cout << "--\n";
    std::cout << "ppj = ##" << ppj->x() << "##\n";
    std::cout << "ppk = ##" << ppk->x() << "##\n";
    std::cout << "ppl = ##" << ppl->x() << "##\n";
    Le résultat, c'est ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    Constructeur de base appele : @@G@@
    pj->x() = ##G[0805edc8]##
    --
    --
    Constructeur de copie appele : ##G##
    --
    Constructeur de copie appele : ##G##
    --
    ppj = ##G[0805edc8]##
    ppk = ##Copie de G[0805eee0]##
    ppl = ##Copie de G[0805ef50]##
    On constate que le cast dans sa première forme fait appel au déréférencement constant, et que le cast dans sa seconde forme fait appel au déréférencement non-constant (celui qui crée une copie). Pour le dernier, vos explications ont déja été satisfaisantes.

    Mais... pourquoi le fait de demander explicitement un pointeur non-constant appelle-t'il le déréférencement constant (pour caster ensuite en pointeur non-constant), et pourquoi le fait de demander explicitement un pointeur constant appelle-t'il le déréférencement normal, pour ensuite le caster en pointeur constant ?

    J'avoue que cela me laisse assez perplexe.

  13. #13
    Membre averti
    Profil pro
    Inscrit en
    Août 2007
    Messages
    32
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2007
    Messages : 32
    Par défaut
    Bon, en fait, dans le code de la question précédente, j'avais un souci de copier-coller. Je pointais sur une autre variable, ce qui pourrissait le test :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    QSharedDataPointer<TestSharedData> pk = pg;
    QSharedDataPointer<TestSharedData> pl = pg;
    Affectation de pg au lieu de pk


    J'ai corrigé mon code, et refait le test. En fait, les trois formes réagissent identiquement, ce qui semble normal : Il doit évaluer d'abord le déréférencement (en prenant, chaque fois, la version non-constante), puis changer ensuite, via le cast, le type.


    Donc le souci est résolu. Merci !

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

Discussions similaires

  1. Réponses: 6
    Dernier message: 15/11/2007, 12h31
  2. Réponses: 4
    Dernier message: 13/06/2007, 15h27
  3. [jdbc][oracle] conversion implicite erronée
    Par Jack Huser dans le forum JDBC
    Réponses: 2
    Dernier message: 30/06/2005, 10h23
  4. [Struts]comment forcer un Forward ?
    Par njac dans le forum Struts 1
    Réponses: 4
    Dernier message: 13/10/2004, 15h02
  5. [MDI] comment fermer la bonne fenêtre enfant ?
    Par KThrax dans le forum Langage
    Réponses: 5
    Dernier message: 01/09/2002, 09h42

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