2009-11-25 8 views
9

J'ai un membre de la classe myMember qui est un myType pointeur. Je veux assigner ce membre dans une fonction déclarée comme const. Je fais comme suit:Affectation de membres dans une fonction const

void func() const 
{ 
    ... 
    const_cast<myType*>(myMember) = new myType(); 
    ... 
} 

Faire cela fonctionne très bien dans VC++, mais GCC donne une erreur avec le message « lvalue requis comme opérande gauche d'affectation ».

Faire le membre mutable me permettent de supprimer simplement le const_cast et affecter la valeur. Cependant, je ne suis pas entièrement sûr que cela vient avec d'autres effets secondaires. Puis-je affecter mon membre sans avoir à rendre le membre mutable? Comment? Y a-t-il des effets secondaires en rendant les membres mutables?

Répondre

8

Le code ne fonctionnera pas réellement dans VC++ - vous n'êtes pas mise à jour de la valeur (ou du moins il ne devrait pas), d'où l'avertissement du CCG. code correct est

const_cast<myType*&>(myMember) = new myType(); 

ou [d'autres réponses, merci: P]:

const_cast<ThisType*>(this)->myMember = new myType(); 

Rendre mutable signifie effectivement que vous obtenez const_cast implicites s dans const fonctions membres, ce qui est généralement ce que vous devriez direction vers quand vous vous trouvez en train de faire des charges de const_cast s sur this. Il n'y a pas «d'effets secondaires à l'utilisation de mutable» autre que cela.

Comme vous pouvez le voir à partir des débats véhéments autour de cette question, l'utilisation de mutable et de beaucoup de s peuvent certainement être des symptômes de mauvaises odeurs dans votre code. D'un point de vue conceptuel, le rejet de constness ou l'utilisation de mutable peut avoir des implications beaucoup plus importantes. Dans certains cas, la bonne chose à faire peut être de changer la méthode en non-const, c'est-à-dire d'assumer le fait qu'elle modifie l'état.

Tout dépend de la façon dont les questions beaucoup const-exactitude dans votre contexte - vous ne voulez pas finir juste sprinking mutable autour comme la poussière de Pixie pour faire le travail de choses, mais mutable est destiné à un usage si la partie ISNT membre de l'observable état de l'objet. La vue la plus stricte de const-correct serait que pas un seul bit de l'état de l'objet ne peut être modifié (par exemple, cela peut être critique si vous êtes instance est en ROM ...) - dans ces cas, vous ne voulez pas de constness être perdu. Dans d'autres cas, vous pouvez avoir un état externe stocké quelque part à l'extérieur de l'objet - par exemple, un cache spécifique au thread qui doit également être pris en compte pour décider s'il est approprié.

+0

Merci. [min 15 caractères] –

+1

Je pense que pendant que cela fonctionne c'est peut-être la pire solution. Il masque complètement ce qui se passe (en changeant un état interne d'une instance immutable) dans un const-cast dans la fonction membre. "mutable" serait plus clair (ou utilisant des interfaces). –

+1

L'élimination de la constellation conduit à un comportement indéfini. À utiliser à vos risques et périls. Une meilleure solution serait de rendre la méthode non const. –

17

Ce scénario - un changement d'état interne encapsulé qui n'a pas d'impact sur l'état externe (par exemple les résultats de la mise en cache) - est exactement ce que le mot-clé mutable est pour.

+1

Mutable n'est pas bon moyen si vous voulez obtenir l'accès en écriture au membre dans une seule const-méthode. – Staseg

+4

L'état observable en externe de l'objet ne doit pas changer lors de l'écriture dans un objet mutable. Rendre un membre mutable juste pour contourner une erreur dans une fonction const n'est pas légitime si le changement est visible de l'extérieur, et peut conduire à un comportement indéfini. Les utilisations légitimes de mutable incluent la mise en cache, où par exemple le même appel renvoie deux fois le même résultat, mais plus rapidement. – AshleysBrain

5
class Class{ 
int value; 
void func()const{ 
const_cast<Class*>(this)->value=123; 
} 
}; 
+0

Vous avez besoin d'un objet cast à la place d'un membre. – Staseg

+0

Merci. Je vais par le premier code de Ruben, car il semble plus propre. Si je pouvais, je marquerais les deux comme "réponse". –

6

const_cast est presque toujours un signe d'échec de conception. Dans votre exemple, func() ne doit pas être const ou myMember doit être mutable.

Un appelant de func() s'attendra à ce que son objet ne change pas; mais cela signifie "ne pas changer d'une manière qu'elle peut remarquer"; c'est, ne pas changer son état externe.Si le changement myMember ne change pas l'état externe de l'objet, c'est à cela que sert le mot-clé mutable; sinon, func() ne devrait pas être const, parce que vous trahiriez vos garanties de fonction. Rappelez-vous que mutable n'est pas un mécanisme pour circunvent const-justesse; c'est un mécanisme pour l'améliorer.

+1

+1 "const_cast est presque toujours un signe d'échec de conception" –

+0

^Oui, et parfois l'échec est dans une bibliothèque que vous utilisez! J'en utilise un qui prend un pointeur-non-'const' mais (et je le vérifie régulièrement :) ne mute pas l'objet. Sécurisé dans cette connaissance, donc je peux passer n'importe quel 'Obj const &' (y compris un temporaire) à ladite bibliothèque, je l'envelopper dans une fonction qui fait un 'const_cast (& constObj)'. Laide, peut-être - mais plus fidèle à la réalité de ce qui arrive réellement au 'Obj' et me permet de faire mieux avec la bibliothèque ... au moins jusqu'à ce que l'auteur décide d'utiliser C++ plus que C [sigh] –

+0

@underscore_d des utilisations légitimes de 'const_cast': traiter avec C++ imparfait. Si j'avais un cent pour chaque ponter-à-non-const qui devrait être un pointeur-à-const dans le monde ... – Gorpik

1

Comme Steve Gilham a écrit, mutable est la réponse correcte (et courte) à votre question. Je veux juste vous donner un indice dans une direction différente. Peut-être qu'il est possible dans votre szenario d'utiliser une (ou plusieurs) interface? Peut-être vous pouvez assimilez à partir de l'exemple suivant:

class IRestrictedWriter // may change only some members 
{ 
public: 
    virtual void func() = 0; 
} 

class MyClass : virtual public IRestrictedWriter 
{ 
public: 
    virtual void func() 
    { 
    mValueToBeWrittenFromEverybody = 123; 
    } 

    void otherFunctionNotAccessibleViaIRestrictedWriter() 
    { 
    mOtherValue1 = 123; 
    mOtherValue2 = 345; 
    } 

    ... 
} 

Donc, si vous passez à une fonction d'un IRestrictedReader * au lieu d'un const MyClass * il peut appeler func et donc changer mValueToBeWrittenFromEverybody alors mOtherValue1 est une sorte de « const ».

. Je trouve mutable toujours un peu un hack (mais l'utilise parfois).

Questions connexes