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

Qt Discussion :

QMap : operator< de QPoint


Sujet :

Qt

  1. #1
    Membre confirmé
    Avatar de betsprite
    Profil pro
    Inscrit en
    Avril 2010
    Messages
    472
    Détails du profil
    Informations personnelles :
    Âge : 36
    Localisation : France

    Informations forums :
    Inscription : Avril 2010
    Messages : 472
    Points : 528
    Points
    528
    Par défaut QMap : operator< de QPoint
    Bonjour,

    Je manipule actuellement une QMap avec en clé des QPoint.

    J'ai donc redéfinie notamment la méthode "operator<" appelée au moment des insert dans ma map :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    inline bool operator<(const QPoint &p1, const QPoint &p2)
    {
      return ((p1.x() < p2.x() && p1.y() <= p2.y()) ||
              (p1.x() =< p2.x() && p1.y() < p2.y()));
    }
    Malgré tout, j'observe deux comportements que je n'arrive pas bien à comprendre :

    1) En mode debug, j'observe que l'operator fait appel à :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    template <class Key> inline bool qMapLessThanKey(const Key &key1, const Key &key2)
    {
        return key1 < key2;
    }
    Mais là, bien que key1 vaut bien QPoint(50,22) et key2 QPoint(160,22), il semble que la valeur key1<key2 ne soit pas définie. J'observe après une boucle entre l'operator et le qMapLessThanKey.

    2) Après un premier élément inséré dans la map de clé QPoint(50,22), j'observe que l'insertion d'un autre élément de clé QPoint(160,22) va venir écraser le premier élément (toujours un élément dans la map et la valeur est égale à celle du dernier inséré..)

    Avez-vous une explication ?

    Merci

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

    Informations professionnelles :
    Activité : aucun

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


    Ta logique ne semble pas en mesure de fournir un "état unique" nécessaire pour l'opérateur <.

    Si l'on réfléchi à la table de vérité, on a:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
             |         |     (1)    |    (2)     |     (3)     |     (4)     |    (5)    |    (6)    |    (7)
     first   | second  |first.x() < |first.y() < | first.x()<= |first.y() <= | (1) && (4)| (3) && (2)| (5) || (6)
    x() |y() |x() |y() |second.x()  |second.y()  |second.x()   |second.y()   |           |           |         
    -----------------------------------------------------------------------------------------------------------------
    10  |10  |15  |15  |true        |true        |true         | true        |   true    |    true   | true ==> ok
    10  |10  |15  |10  |true        |false       |true         | true        |   true    |    false  | true ==> ok
    10  |15  |15  |10  |true        |false       |true         | false       |   false   |    false  | false ==> oops
    15  |10  |15  |15  |false       |true        |true         | true        |   false   |    true   | true ==> ok
    15  |10  |15  |10  |false       |false       |true         | true        |   false   |    false  | false ==> ok
    15  |15  |15  |10  |false       |false       |true         | false       |   false   |    false  | false ==> ok
    15  |10  |10  |15  |false       |true        |false        | true        |   false   |    false  | false ==> ok
    15  |15  |10  |15  |false       |false       |false        | true        |   false   |    false  | false ==> ok
    15  |15  |10  |10  |false       |false       |false        | false       |   false   |    false  | false ==> ok
    Comme tu peux le constater, il y a un moment où la table de vérité nous donne un résultat incohérent: c'est quand le premier point a x == 10 et y == 15 et que le second point a x==15 et y == 10

    On se serait en effet attendu à ce que le résultat vale... true

    Généralement, lorsqu'il s'agit de combiner plusieurs valeurs dans un test "plus petit que", on travaille sur une logique de l'ordre de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    bool operator <(QPoint const & first, QPoint const & second)
    {
        return first.x() <second.x() || 
               ( first.x() == second.x() && first.y() < second.y() ) ;
    }


    NOTA: Personnellement, je ne crois pas que je créerais un opérateur <, mais plutôt un foncteur clairement nommé qui fournisse le résultat.

    On peut en effet discuter le choix de prioriser la valeur de x par rapport à celle de y, et il n'est donc pas impossible que, tôt ou tard, tu veuilles avoir un tri qui donne la priorité à y.

    En définissant l'opérateur < pour l'une des possibilités, tu places une restriction forte en terme d'évolution, que tu pourrais très facilement éviter en utilisant un foncteur

  3. #3
    Membre confirmé
    Avatar de betsprite
    Profil pro
    Inscrit en
    Avril 2010
    Messages
    472
    Détails du profil
    Informations personnelles :
    Âge : 36
    Localisation : France

    Informations forums :
    Inscription : Avril 2010
    Messages : 472
    Points : 528
    Points
    528
    Par défaut
    Salut koala01 et merci pour ton aide

    Effectivement, la table de vérité présente des cas non gérés correctement..

    Merci également pour l'idée du foncteur qui semble la plus intéressante en terme d'évolution!

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 626
    Points : 30 684
    Points
    30 684
    Par défaut
    Citation Envoyé par betsprite Voir le message
    Salut koala01 et merci pour ton aide

    Effectivement, la table de vérité présente des cas non gérés correctement..

    Merci également pour l'idée du foncteur qui semble la plus intéressante en terme d'évolution!
    Avec palisir

  5. #5
    Membre du Club
    Profil pro
    Inscrit en
    Mars 2012
    Messages
    99
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2012
    Messages : 99
    Points : 58
    Points
    58
    Par défaut
    Bonjour,

    Je me suis intéressé au problème et j'ai essayé de faire quelque chose de similaire. Apparemment, pour profiter d'un foncteur avec QMap, on doit d'abord créer une std::map avec ce foncteur et ensuite la passer au constructeur de la QMap.

    J'ai donc :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
        class Less // foncteur
        {
          public :
            bool operator()(const QPoint& p1, const QPoint& p2)
            {
              return p1.x() < p2.x() ||
                      (p1.x() == p2.x() && p1.y() < p2.y());
            }
        };
    Puis ensuite, dans le constructeur de la class ou j'ai ma QMap :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
          Less lessFunctor;
          std::map<QPoint, Obj, Less> map = std::map<QPoint, Obj, Less>();
          m_map = QMap<QPoint, Obj>(map);
    Mais j'obtiens l'erreur :

    erreur : no matching function for call to 'QMap<QPoint, Obj>::QMap(std::map<QPoint, Obj, Less>&)'
    Je dois mal m'y prendre pour l'utilisation du foncteur j'imagine?
    Après dans les constructeurs proposés par QMap, on a bien un std::map mais seulement avec la clé et la valeur indiquées, pas de foncteurs possibles en troisième argument ?

    Merci

    PS : Obj représente juste un objet dont la connaissance n'est pas utile ici.

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 626
    Points : 30 684
    Points
    30 684
    Par défaut
    C'est parce que tu t'y prend mal...


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
          Less lessFunctor;
          std::map<QPoint, Obj, Less> map; // laisse le constructeur par défaut faire son job ici
          m_map = map; // laisse l'opérateur d'affectation faire son job ici
    En plus, dans le constructeur d'une classe, il est préférable d'utiliser les listes d'initialisation:
    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
    class MaClass : public QWidget{
        public:
            MaClass(QWidget * parent):QWidget(parent){ // m_map est créé automatiquement
                                                       // avec son constructeur par défaut
                /*rien à faire ici */
            }
            /* OU OU OU / ET ET ET */
            MaClass(QWidget * parent, QMap<QPoint, Obj, Less> const & map):
                QWidget(parent), m_map(map){ // m_map est créé en utilisant le 
                                             // constructeur par copie 
                /* rien à faire ici */
            }
        private:
            QMap<QPoint, Obj, Less> m_map;
    };

  7. #7
    Membre du Club
    Profil pro
    Inscrit en
    Mars 2012
    Messages
    99
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2012
    Messages : 99
    Points : 58
    Points
    58
    Par défaut
    Effectivement, plusieurs points n'étaient pas terribles.

    Néanmoins, le compilateur renvoie bien une erreur lorsqu'on essaie d'ajouter en argument de template à QMap le foncteur :

    erreur : wrong number of template arguments (3, should be 2)
    Aussi, pour l'assignation entre le std::map et le QMap, évidemment, l'opérateur d'affectation demande à être redéfinie. Et justement, en la redéfinissant, comment faire passer le foncteur à QMap ?

    Merci encore koala01

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 626
    Points : 30 684
    Points
    30 684
    Par défaut
    Ouppsss... Stop...

    Je sais que la map de la STL permet d'accepter un troisième paramètre template qui correspond au comparateur.

    Mais il me semble que QMap ne prend d'office que deux paramètres (le type de la clé et celui de la valeur).

    Enfin, il n'existe aucun constructeur de QMap prenant une std::map en paramètre (il est donc impossible de convertir l'un en l'autre de manière automatique)

    Tu pourrais donc assez facilement utiliser une std::map<QPoint, Obj, Comparator> en définissant Compartor comme étant un foncteur qui compare des QPoint, mais il te serait impossible d'utiliser la classe QPoint comme clé d'une QMap

    Par contre, rien ne t'empêche de créer une classe personnalisée qui fournisse un opérateur < , et qui manipule un QPoint de manière transparente:

    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
    class PointLess{
        public:
            PointLess(QPoint const & p): p(p){}
            QPoint operator() (){return p;}
            QPoint const & operator()() const{return p;}
            bool operator < (PointLess const & rhs) const
            {
                return p.x() < rhs.p.x() ||
                       ( p.x()== rhs.p.x() && p.y() < rhs.p.y());
            }
            bool operator < (QPoint const & rhs ) const{
                return (*this) < PointLess(rhs);
            }
        private:
            QPoint p;
    };
    A ce moment là, tu devrait pouvoir avoir une classe proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    class MyClass : public QWidget{
        public:
            MyClass(QWidget * parent):QWidget(parent){}
            MyClass(QWidget * parent, QMap<PointLess, Obj> const & map):
                QWidget(parent), m_map(map){}
        private:
            QMap<PointLess, Obj> m_map;
    };

  9. #9
    Membre du Club
    Profil pro
    Inscrit en
    Mars 2012
    Messages
    99
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2012
    Messages : 99
    Points : 58
    Points
    58
    Par défaut
    Enfin, il n'existe aucun constructeur de QMap prenant une std::map en paramètre (il est donc impossible de convertir l'un en l'autre de manière automatique)
    Bizarre, par exemple ici, on voit clairement :
    QMap ( const std::map<Key, T> & other )
    Tu pourrais donc assez facilement utiliser une std::map<QPoint, Obj, Comparator> en définissant Compartor comme étant un foncteur qui compare des QPoint, mais il te serait impossible d'utiliser la classe QPoint comme clé d'une QMap
    Tu voulais surement dire une std::map à la fin de ta phrase non ?

    Par contre, rien ne t'empêche de créer une classe personnalisée qui fournisse un opérateur < , et qui manipule un QPoint de manière transparente:
    Ah oui pas mal. En gros, on peut aussi passer par la redéfinition de l'opérateur < d'un point au lieu de l'opérateur < entre deux points.
    Par contre ici, je perds l'intention du foncteur. Dans ce cas, pourquoi ne pas rester avec un opérateur < qui compare deux points ?

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 626
    Points : 30 684
    Points
    30 684
    Par défaut
    Citation Envoyé par sfarc Voir le message
    Bizarre, par exemple ici, on voit clairement :
    Si ce n'est que cela se base sur le fait que la clé est naivement comparable (qu'il existe un opérateur < pour la clé), alors que, pour pouvoir comparer un QPoint à un autre, il faut préciser le comparateur à utiliser
    Tu voulais surement dire une std::map à la fin de ta phrase non ?
    Non, je voulais bel et bien dire QMap.

    D'un coté, tu as la std::map qui est définie sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    template <typename clé, typenale valeur, typename comparateur = std::less>
    class map{/*...*/};
    qui te permet de permet de préciser le foncteur à utiliser pour la comparaison ( et qui utilise par défaut std::less, qui fait, par défaut, appel à l'opérateur < de la clé) et, de l'autre coté, tu a la QMap qui est définie sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    template <typename cle , typename valeur> 
    class QMap{/* ... */};
    qui, ne permettant pas de préciser le comparateur, doit sans doute se baser sur le principe que la clé fournira d'office un opérateur < )
    Ah oui pas mal. En gros, on peut aussi passer par la redéfinition de l'opérateur < d'un point au lieu de l'opérateur < entre deux points.
    Par contre ici, je perds l'intention du foncteur. Dans ce cas, pourquoi ne pas rester avec un opérateur < qui compare deux points ?
    Parce que si tu crées l'opérateur < sur ton QPoint, tu te trouves dans une situation dans laquelle la comparaison est "verrouillée" sur une seule logique clairement définie "ad vitam".

    Par contre, si tu crées une autre classe proche de
    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
    class PointLessYFirst{
        public:
            PointLessYFirst(QPoint const & p): p(p){}
            QPoint operator() (){return p;}
            QPoint const & operator()() const{return p;}
            bool operator < (PointLess const & rhs) const
            {
                return p.y() < rhs.p.y() ||
                       ( p.y()== rhs.p.y() && p.x() < rhs.p.x());
            }
            bool operator < (QPoint const & rhs ) const{
                return (*this) < PointLess(rhs);
            }
        private:
            QPoint p;
    };
    tu pourras effectivement déclarer une instance de QMap<PointLessYFirst, Obj> pour laquelle les objet seront triés par leur ordonnées en priorité, puis par leur abscisses

    EDIT : tu pourrais donc parfaitement avoir une classe proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    template <typename PointCompare>
    class MyClass : public QWidget{
        public:
            MyClass(QWidget * parent):QWidget(parent){}
            MyClass(QWidget * parent, QMap<PointCompare, Obj> const & map):
                QWidget(parent), m_map(map){}
        private:
            QMap<PointCompare, Obj> m_map;
    };
    qui utilisera le paramètre PointCompare indiqué pour trier tes différents objets

  11. #11
    Membre du Club
    Profil pro
    Inscrit en
    Mars 2012
    Messages
    99
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2012
    Messages : 99
    Points : 58
    Points
    58
    Par défaut
    de l'autre coté, tu a la QMap qui est définie sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    template <typename cle , typename valeur> 
    class QMap{/* ... */};
    qui, ne permettant pas de préciser le comparateur, doit sans doute se baser sur le principe que la clé fournira d'office un opérateur < )
    Ok je vois mais on peut tout de même se demander pourquoi les développeurs de QMap n'ont pas permis la même "logique" que pour std::map avec un template aussi fourni. Après bon c'est vrai qu'en passant par l'opérateur de la clé le problème est réglé

    EDIT : tu pourrais donc parfaitement avoir une classe proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    template <typename PointCompare>
    class MyClass : public QWidget{
        public:
            MyClass(QWidget * parent):QWidget(parent){}
            MyClass(QWidget * parent, QMap<PointCompare, Obj> const & map):
                QWidget(parent), m_map(map){}
        private:
            QMap<PointCompare, Obj> m_map;
    };
    qui utilisera le paramètre PointCompare indiqué pour trier tes différents objets
    C'est tout aussi bien que le foncteur en fait!

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 626
    Points : 30 684
    Points
    30 684
    Par défaut
    Citation Envoyé par sfarc Voir le message
    Ok je vois mais on peut tout de même se demander pourquoi les développeurs de QMap n'ont pas permis la même "logique" que pour std::map avec un template aussi fourni. Après bon c'est vrai qu'en passant par l'opérateur de la clé le problème est réglé
    Je crois que c'est surtout un problème "historique".

    Les premières versions de Qt datent me semble-t-il d'avant la mise au point de la norme dans laquelle map a été définie, et les concepteurs de QMap n'ont peut etre simplement pas été aussi loin dans leur raisonnement que ceux de std::map

    C'est tout aussi bien que le foncteur en fait!
    Ce n'est que l'application de la phrase de David Wheeler:
    Citation Envoyé par David Wheeler
    all problems in computer science can be solved by another level of indirection .

    Tout problème en science informatique peut etre résolu par un autre niveau d'indirection

  13. #13
    Membre du Club
    Profil pro
    Inscrit en
    Mars 2012
    Messages
    99
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2012
    Messages : 99
    Points : 58
    Points
    58
    Par défaut
    Ce n'est que l'application de la phrase de David Wheeler:

    Envoyé par David Wheeler
    all problems in computer science can be solved by another level of indirection .

    Tout problème en science informatique peut etre résolu par un autre niveau d'indirection
    Exactement .

    Merci encore koala01

  14. #14
    Membre du Club
    Profil pro
    Inscrit en
    Mars 2012
    Messages
    99
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2012
    Messages : 99
    Points : 58
    Points
    58
    Par défaut
    Bon, je reviens à nouveau sur ce poste car j'ai un problème lié à ma nouvelle QMap .

    Voici ma map perso :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
        template <typename PointCompare>
        class Map : public QMap<PointCompare, Obj>
        {/* ... */};
    Ensuite, à un moment, je veux itérer sur ma map créée. Je fais donc :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
          Map<PointLess>::iterator it;
          for (it = m_map.begin(); it != m_map.end(); ++it)
          {}
    Mais là, j'obtiens une erreur avec l'opérateur "=" sur l'itérateur, je ne comprends pas bien pourquoi.

    erreur : no match for 'operator=' in 'it = m_map<PointLess>::<anonymous>.QMap<Key, T>::begin<PointLess, Obj>()'
    Il manquerait quoi comme information dans la définition de ma map ?
    Ou alors je dois redéfinir mon propre iterator avec l'operateur "=" justement. Cela voudrait dire en gros qu'à chaque fois qu'on définit sa propre map, il faut redéfinir un iterator associé c'est bien ça ?
    Du coup, comment est défini l'itérateur par défaut lorsqu'on fait
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Map<PointLess>::iterator it;
    ?

    Merci

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

Discussions similaires

  1. operator>> pour une QMap avec qt dbus
    Par bourriquet_42 dans le forum Qt
    Réponses: 1
    Dernier message: 18/02/2009, 17h49
  2. afficher la signature des opérations dans XDE
    Par ChristopheH dans le forum Rational
    Réponses: 1
    Dernier message: 24/05/2004, 15h41
  3. [JSP] thread ? Message d'avancement des operations en cours
    Par buffyann dans le forum Servlets/JSP
    Réponses: 14
    Dernier message: 18/12/2003, 11h39
  4. operation sur des alias
    Par 74160 dans le forum Requêtes
    Réponses: 4
    Dernier message: 24/11/2003, 18h19
  5. Réponses: 8
    Dernier message: 21/11/2003, 18h38

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