2010-05-26 4 views
2

C'est ce que je suis en train de faire et je ne peux pas:Comment défausser const en C++

#include <string> 
using namespace std; 
class A { 
public: 
    bool has() const { return get().length(); } 
    string& get() { /* huge code here */ return s; } 
private: 
    string s; 
}; 

L'erreur que je reçois est:

passing ‘const A’ as ‘this’ argument of 
‘std::string& A::get()’ discards qualifiers 

Je comprends ce que le problème est, mais comment puis-je le réparer? J'ai vraiment besoin de has() pour être const. Merci.

+0

Juste pour vous rappeler que les membres mutables peuvent être modifiés par const, s o il pourrait être logique d'avoir get() const (mais probablement de ne pas renvoyer un ref non const) même s'il doit y modifier s dans le code énorme. – AProgrammer

+0

Quel genre d'effets secondaires obtenez-vous? –

+0

Il modifie évidemment l'art. Sinon, le correctif trivial est 'return s.length()'. Par conséquent, 'has()' just is not const. – MSalters

Répondre

6

Ajouter une seconde surcharge de get():

string const & get() const { return s; } 

qui sera appelée sur un objet typé const de classe A.

En pratique, je préfère ajouter seulementconst des accesseurs de type, puis garder les modifications entièrement internes à la classe ou même les éviter complètement. Par exemple, cela signifie avoir une méthode DoUpdateLabel(){/*do something with s*/} plutôt que d'exposer les internes. Cela a l'effet secondaire agréable que vous pouvez éviter la duplication des accesseurs dans de nombreux cas.

Si vous devez absolument avoir une modification par accesseurs et vous ne voulez pas une enveloppe supplémentaire de const, vous pouvez utiliser const_cast<>:

bool has() const { return const_cast<A*>(this)->get().length(); } 

Cependant, si get() a des effets secondaires et has() est déclaré const, il est douteux que ce soit le comportement que vous voulez vraiment.

+0

Non; ** changer ** obtenir(). Je ne peux pas penser à une raison pour laquelle vous voudriez écrire 'a.get() + =" abc "'. –

+0

:-) - d'accord complètement - c'est exactement ce que je venais d'ajouter comme un montage! –

+0

Je ne peux pas avoir deux versions de 'get()' parce que ma fonction (dans le projet de la vie réelle) est plutôt grande. Je ne veux pas dupliquer le code. – yegor256

2

Il est assez courant d'avoir les versions const et non-const des mêmes fonctions. Donc, l'un d'eux serait appelé si votre objet est appelé en tant qu'objet const, et le second - si la même chose se produit avec un non-const.

Je dois mentionner que cette technique est généralement utilisée avec les opérateurs mais convient également pour votre cas actuel.


Aussi, dans votre cas actuel, la meilleure approche pourrait signifier avoir SEULEMENT const méthode comme

const string& get() const { return s } 

et refactorisation tous vos autres code afin qu'il fonctionne avec une chaîne retournée par référence constante.

+0

+1: nous sommes évidemment sur le même message ici :-) –

+0

Je ne peux pas retourner 'const string &' parce que j'utilise cette chaîne plus tard, en la changeant. Et je ne peux pas avoir deux fonctions avec des signatures différentes car elles sont plutôt grandes. Je vais dupliquer le code – yegor256

0
std::string get() const { return s; } 

parce que le passage des chaînes par valeur est généralement pas cher (je ne me souviens pas si cela est une garantie), et je ne pense pas que vous voulez que la chaîne que vous regardez pour changer sous vos pieds lorsque A le modifie.

+0

'string' est juste un exemple. Dans le projet de la vie réelle, j'ai une autre classe et un autre but du code – yegor256

+0

En outre, la copie même de petites chaînes est en fait assez cher en raison de la surcharge de mémoire allocation. Les constructeurs C++ 0x move peuvent corriger cela ... –

+0

AFAIK, std :: string de GCC/libstdC++ est soutenu par un tampon compté par référence, lui donnant un comportement de type copier-sur-écriture: La copie est O (1) et la mutation est bon marché à condition que le compte soit de 1. Bien que ce ne soit pas une bonne idée de s'appuyer sur les détails de mise en œuvre, ce n'est pas la fin du monde non plus. –

0

Essayez ceci:

const string& get() const { return s; } 

La première const applique au type de retour de la méthode get() méthode. Le type de retour est une référence à une chaîne. Dans l'ensemble, il s'agit d'une référence const à une chaîne, qui impose que cette valeur ne soit pas modifiée.

La seconde const s'applique à la méthode, en faisant effectivement une méthode const.

2

Je pense que votre question est un peu vague.

Vous avez:

bool has() const { return get().length(); } 
string& get() { /* huge code here */ return s; } 

... et ont besoin has() être const.

je peux penser à trois façons de contourner cela, en fonction de ce que vous essayez vraiment faire:

L'option la plus propre serait pour has d'utiliser uniquement le code const. Si certains de vos /* huge code here */ code non-const mais ne change pas réellement la valeur logique de la classe (comme le calcul d'une valeur interne mise en cache) envisagez d'utiliser mutable sur les données impliquées.

Si la partie /* huge code here */ est non const par nature, considère refactorisation dans une autre fonction et de l'appeler séparément:

using namespace std; 
class A { 
public: 
    bool has() const { return get().length(); } 
    const string& get() const { return s; } 
    void computestuff() { /* huge code here */ } 
private: 
    string s; 
}; 

A instance; 

// old client code: 
// string s = instance.get(); 
// 
// new code: 
instance.computestuff(); // non-const call 
instance.get(); // const call 
instance.has(); // const call 

En fin de compte, vous pouvez également jeter le const-ness loin (mais gardez à l'esprit que la nécessité de le faire est presque toujours un signe de mauvaise conception/besoin de refactoriser):

class A { 
public: 
    bool has() const { return const_cast<A*>(this)->get().length(); } 
    string& get() { /* huge code here */ return s; } 
private: 
    string s; 
}; 
+0

Ce serait aussi ma solution * préférée * - bien que cela ne ressemble pas à l'OP qui penche dans cette direction. –