IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C++ Discussion :

Outrepasser le "Constness" du pointeur "this" d'une méthode sans utiliser "mutable"


Sujet :

C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre actif
    Profil pro
    Inscrit en
    Mai 2009
    Messages
    45
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2009
    Messages : 45
    Par défaut Outrepasser le "Constness" du pointeur "this" d'une méthode sans utiliser "mutable"
    Bonjour,
    J'ai un problème avec une méthode 'const':

    Soit une classe M2 (qui représente une matrice), qui dérive d'une classe M. Cette classe dispose d'une méthode de normalisation, qui doit... normaliser, c'est à dire modifier la valeur des attributs, mais sans changer la "valeur globale" (ceux qui ont entendus parler de coordonnées homogènes me comprendront, pour les autres, c'est pas grave...)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    struct M2 : public M
    {
        void Normalize() const;
    };
    Cette méthode doit être 'const' parce qu'utilisée dans d'autre méthodes 'const'. En général, la parade consiste à déclarer les attributs concernés comme 'mutable'. Oui, mais ici, on n'y a pas accès, ils sont hérités (et on ne peux pas modifier la classe de base M).

    Cette normalisation consiste à diviser tous les termes par un coefficient, et pour ça, la classe de base me fournit une surdéfinition de l'opérateur '/':
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    struct M : public autre_classe
    {
        M operator / ( double v ) const;
    };
    Ce que je veut écrire comme implémentation, c'est quelque chose comme:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    void M2::Normalize() const
    {
        double k = ... // on calcule la bonne valeur
        *this = *this / k;
    }
    Mais évidemment, le compilo me jette, car this est un pointeur 'const', et donc interdit d'utiliser l'opérateur d'affectation sur this déréferencé...

    Je me doute qu'il y a une bidouille à faire avec un const_cast, mais j'ai essayé plusieurs trucs, et je n'arrive pas à m'en sortir. Il faudrait que je puisse enlever le caractère 'const' de this, mais je ne suis pas sûr que ça soit possible.

    Je patauge un peu, là, merci pour votre aide.

  2. #2
    Responsable 2D/3D/Jeux


    Avatar de LittleWhite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2008
    Messages
    27 122
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

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

    Informations forums :
    Inscription : Mai 2008
    Messages : 27 122
    Billets dans le blog
    148
    Par défaut
    Bonjour,

    J'ai l'impression que c'est plus un problème de conception.
    Une fonction qui va modifier les valeurs de la classe, ne peut pas être const. Ce qui veut dire, que une fonction de normalisation ne peut pas être const. Sinon, vous essayez de briser une des règles de base du CPP ...

    J'imagine que cela ne vous arrange pas, mais il faudra enlever le const pour toutes les fonctions qui appele la fonction Normalize()
    Vous souhaitez participer à la rubrique 2D/3D/Jeux ? Contactez-moi

    Ma page sur DVP
    Mon Portfolio

    Qui connaît l'erreur, connaît la solution.

  3. #3
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Salut,

    Je rejoint LittleWhite dans le sens où tu devrais te baser sur le fait que les matrices constantes devraient être réputées normalisées.

    Ceci dit, la solution pourrait être que, normalement, l'opérateur / est sensé renvoyer un nouvel objet (c'est l'opérateur /=, s'il existe, qui devrait renvoyer l'objet courent modifié).

    Tu pourrais donc baser ta réflection sur le const_cast, bien que je conseille très sériseusement de partir du principe que les matrices constantes ont été normalisées
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    void M::normalise() const
    {
        double k;
        /*...*/ 
        M temp= (*this) / k; // crée une nouvelle matrice qui sera normalisée
        const_cast<Matrice&>(*this)=temp; //supprime la constance de *this et assigne temp à *this
    };
    [EDIT]D'ailleurs, à la réflexion, normalize devrait, elle aussi, renvoyer un nouvel objet qui serait la copie normalisée de l'objet courent, ce qui te permettrait, entre autre, de garder la matrice d'origine "en l'état" (non normalisée)...

    Mais, de ce fait, le problème sera sans doute reporté dans les fonctions membres constantes qui font appel à cette fonction...

    Cela donnerait un code proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    M M::normalize() const
    {
        int k;
        /*...*/
        M temp= (*this)/k;
        return temp;
    }
    [/EDIT]
    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. #4
    Membre chevronné
    Inscrit en
    Août 2004
    Messages
    556
    Détails du profil
    Informations forums :
    Inscription : Août 2004
    Messages : 556
    Par défaut
    Si ta matrice normalisée a un comportement (lire: est différent d'un certain sens, ne réagit pas pareil, renvoit des valeurs différentes, etc...) différent d'une matrice non normalisée, alors ta méthode normalisée ne devrait pas être const, comme l'ont déjà dit les 2 personnes avant moi.

    le mot clé mutable, c'est vraiment pour un but spécifique et non visible extérieurement. Les attributs mutables sont en général utilisés pour régler des problèmes d'implémentation uniquement (pour un cache interne par exemple).

  5. #5
    Membre actif
    Profil pro
    Inscrit en
    Mai 2009
    Messages
    45
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2009
    Messages : 45
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    void M::normalise() const
    {
        double k;
        /*...*/ 
        M temp= (*this) / k; // crée une nouvelle matrice qui sera normalisée
        const_cast<Matrice&>(*this)=temp; //supprime la constance de *this et assigne temp à *this
    };
    Super, c'est exactement ce que je cherchais à obtenir ! C'était le &-référence sur l'objet qui me manquait !
    D'autre part, c'est plus logique d'ajouter la méthode de normalisation dans la classe de base, plutôt que dans la classe dérivée (ce que j'ai fait).

    Bon, après essais, on peut même se passer de la première copie par valeur, qui pénaliserait les perfs, et écrire directement:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    void M::normalise() const
    {
        double k;
        /*...*/ 
        const_cast<M&>(*this) = (*this) / k;
    };

    Citation Envoyé par koala01 Voir le message
    [EDIT]D'ailleurs, à la réflexion, normalize devrait, elle aussi, renvoyer un nouvel objet qui serait la copie normalisée de l'objet courent, ce qui te permettrait, entre autre, de garder la matrice d'origine "en l'état" (non normalisée)...
    [/EDIT]
    En fait, il y a une ambiguité dans la notion de normalisation. Une matrice homogène normalisée "représente" la MEME CHOSE que la même non-normalisée (d'ou le const...). La normalisation ne fait que "recadrer" les valeurs numériques de façon à, d'une part, limiter les erreurs d'arrondi dues aux très grandes ou très petites valeurs, d'autre part, permettre des comparaisons, en utilisant directement les valeurs numériques.

    Par exemple, le vecteur homogène 3x1 : [ 2 3 4] est strictement identique à [4 6 8]
    Voili, voila pour la petite histoire, pour plus de détails là-dessus, on pourra voir:
    [ame]http://en.wikipedia.org/wiki/Homogeneous_coordinates[/ame]

    Merci à tous.

  6. #6
    Membre actif
    Profil pro
    Inscrit en
    Mai 2009
    Messages
    45
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2009
    Messages : 45
    Par défaut
    Citation Envoyé par LittleWhite Voir le message
    Bonjour,
    Une fonction qui va modifier les valeurs de la classe, ne peut pas être const. Ce qui veut dire, que une fonction de normalisation ne peut pas être const.
    Je précise: dans ce type de matrices, ce qui compte c'est la valeur relative des différents coefficients. Une fonction de normalisation va modifier les valeurs, mais laisser les rapports constants. Ce qui explique pourquoi elle est 'const', bien qu'elle modifie les valeurs des attributs: les "sens" des valeurs reste constant, la matrice représente toujours la même valeur.
    Jusqu'à présent, le mot-clé mutable me sauvait la mise...

    Et elle doit rester const, parce qu'elle est appelée dans tout un tas de méthodes constantes...

  7. #7
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Mais la matrice "d'origine" a des valeur absolues différentes de la matrice normalisée...

    En effet, si tu as dans ta matrice une fonction membre qui renvoie la valeur d'une cellule de la matrice (par exemple ceill(unsigned int line, unsigned int col)), tu ne peux pas dire que
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    mat.ceill( 3, 3 ) == mat.normalize().ceill( 3, 3)
    du fait que la cellule 3,3 aura été modifiée par un coefficient donné.

    L'idée, si tu as besoin de travailler sur une matrice dont tu sera sur qu'elle est normallisée est donc de travailler sur une copie normalisée de la matrice passée en paramètre:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    void doSomething( Matrice const & mat)
    {
       /* nous n'allons pas changer mat, mais nous travaillons sur une nouvelle
        * matrice équivalente suite à la normalisation 
        */
       Matrice /* const */ temp = mat.normalize();
       /* tout ce que l'on fait ici sera fait au départ de temp, et non de au 
        * depart de mat
        */
    }//destruction automatique de la matrice temporaire
    Et cela peut fonctionner avec les fonctions membres de matrice:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    void Matrice::doSomething() /* const */
    {
        /* créons une matrice normalisée au départ de la matrice courente 
         */
        Matrice /* const */ temp=normalize();
        /* nous ne travaillons pas depuis this, mais depuis temp
         */ 
    }//destruction automatique de la matrice temporaire
    Le tout en se basant sur le fait que la fonction normalize() renvoie... une matrice différente de la matrice courante sur laquelle la fonction est appliqué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

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Il faut bien comprendre que, lorsque tu déclares une fonction constante, tu t'engage formellement à ne modifier ni l'objet courent ni son contenu...

    Or, la fonction normalize ne modifie peut être pas l'objet courent dans le sens où les résultats matriciels sont identiques, mais elle modifie le contenu, dans le sens ou une cellule donnée prise au hasard aura, fatalement, une valeur différente avant et après normalisation.

    Et il faut bien te dire que le compilateur est un brave soldat qui sera beaucoup plus buté que toi: si tu t'es engagé à ne pas modifier l'objet courent, il refusera systématiquement que tu lui donne une instruction contraire à ton engagement
    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. #9
    Membre actif
    Profil pro
    Inscrit en
    Mai 2009
    Messages
    45
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2009
    Messages : 45
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Mais la matrice "d'origine" a des valeur absolues différentes de la matrice normalisée...
    Toutafé...

    Mais en général, on s'arrange pour n'avoir QUE des matrices normalisées... Cette fameuse méthode est d'ailleurs déclarée en "protected", car ce n'est pas à "l'extérieur" d'aller faire ça, mais plutôt à chaque opération interne de faire un petit coup de Normalise() après son petit calcul.

    Citation Envoyé par koala01 Voir le message
    L'idée, si tu as besoin de travailler sur une matrice dont tu sera sur qu'elle est normallisée est donc de travailler sur une copie normalisée de la matrice passée en paramètre:
    Oui, dans l'idée, pourquoi pas, on pourrait effectivement demander à chaque calcul de faire ça, mais du coup on se trimballe en permanence la matrice non-normalisée, ce qui est un peu à contre-sens de l'idée de coordonnées homogène normalisées... Et à un moment, on risque d'accumuler les erreurs, et se retrouver avec une matrice normalisée qui ne représente que du "garbage".

    Citation Envoyé par koala01 Voir le message
    Il faut bien comprendre que, lorsque tu déclares une fonction constante, tu t'engage formellement à ne modifier ni l'objet courent ni son contenu...
    Oui, c'est pour ça d'ailleurs qu'on a inventé le 'mutable' ! Pour pouvoir ne pas respecter les règles usuelles

    Citation Envoyé par koala01 Voir le message
    Et il faut bien te dire que le compilateur est un brave soldat qui sera beaucoup plus buté que toi: si tu t'es engagé à ne pas modifier l'objet courent, il refusera systématiquement que tu lui donne une instruction contraire à ton engagement
    Merci pour l'info ;-)

    Mais justement, ta solution montre bien qu'on peut outrepasser ces règles

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Citation Envoyé par akirira Voir le message
    Toutafé...

    Mais en général, on s'arrange pour n'avoir QUE des matrices normalisées... Cette fameuse méthode est d'ailleurs déclarée en "protected", car ce n'est pas à "l'extérieur" d'aller faire ça, mais plutôt à chaque opération interne de faire un petit coup de Normalise() après son petit calcul.

    Oui, dans l'idée, pourquoi pas, on pourrait effectivement demander à chaque calcul de faire ça, mais du coup on se trimballe en permanence la matrice non-normalisée, ce qui est un peu à contre-sens de l'idée de coordonnées homogène normalisées... Et à un moment, on risque d'accumuler les erreurs, et se retrouver avec une matrice normalisée qui ne représente que du "garbage".
    Alors, l'idée serait plutot de normaliser une fois pour toute la matrice après l'avoir créée et d'abandonner la matrice d'origine au profit de la matrice normalisée...

    Et de ne même plus faire appel à la matrice d'origine
    Oui, c'est pour ça d'ailleurs qu'on a inventé le 'mutable' ! Pour pouvoir ne pas respecter les règles usuelles
    Attention, le mot clé mutable est à utiliser avec énormément de précautions

    Typiquement, il n'est à utiliser que lorsque tu as, à certains moments, besoin d'une représentation différente de ta donnée et que cette représentation différente ne doit réévaluée que de manière très ponctuelle: lorsque, par exemple, de modifier régulièrement ton objet, mais que tu n'utilise que très rarement la représentation différente (c'est, typiquement, ce que l'on appelle un buffer)

    On commence alors par vérifier s'il est intéressant de réévaluer le buffer (AKA: si l'objet a été modifié entre la dernière évaluation du buffer et son utilisation actuelle) et par le réévaluer le cas échéant.
    Merci pour l'info ;-)

    Mais justement, ta solution montre bien qu'on peut outrepasser ces règles
    Ma solution montre bien qu'il est possible d'outrepasser ces règles, mais il faut bien comprendre que ce n'est jamais, au mieux, qu'un pis aller, voir un "cache misère" et que cela permet, au mieux, de cacher un problème plus profond... du moins, dans la majorité des cas (et dans le cas présent certainement)
    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

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

Discussions similaires

  1. Réponses: 10
    Dernier message: 28/08/2008, 18h15
  2. [TSQL]Probleme d'insertion d'une chaine (varchar) contenant un simple quote
    Par Anthony.Desvernois dans le forum MS SQL Server
    Réponses: 3
    Dernier message: 04/07/2007, 15h57
  3. Réponses: 11
    Dernier message: 11/04/2007, 18h33

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