Le mot-clé 'mutable' a-t-il un autre but que de permettre à la variable d'être modifiée par une fonction const?
il y a quelque temps, j'ai trouvé un code qui marquait une variable membre d'une classe avec le mot-clé mutable
. Pour autant que je puisse le voir, il vous permet simplement de modifier une variable dans une const
méthode:
class Foo
{
private:
mutable bool done_;
public:
void doSomething() const { ...; done_ = true; }
};
Est-ce la seule utilisation de ce mot-clé ou est-il plus à lui que rencontre l'œil? J'ai depuis utilisé cette technique dans une classe, marquant un boost::mutex
comme mutable permettant aux fonctions const
de le verrouiller pour des raisons de sécurité du fil, mais, pour être honnêtement, on dirait que c'est un peu un piratage.
18 réponses
il permet la différenciation de const bitwise et const logique. Logique const, c'est quand un objet ne change pas d'une manière qui est visible à travers l'interface publique, comme votre verrouillage exemple. Un autre exemple serait une classe qui calcule une valeur la première fois qu'elle est demandée, et cache le résultat.
depuis c++11 mutable
peut être utilisé sur une lambda pour indiquer que les choses capturées par valeur sont modifiables (ils ne sont pas par défaut):
int x = 0;
auto f1 = [=]() mutable {x = 42;}; // OK
auto f2 = [=]() {x = 42;}; // Error: a by-value capture cannot be modified in a non-mutable lambda
le mot-clé mutable
est un moyen de percer le voile const
que vous drapez sur vos objets. Si vous avez une référence const ou un pointeur vers un objet, vous ne pouvez pas modifier cet objet de quelque manière que ce soit sauf quand et comment il est marqué mutable
.
avec votre const
référence ou pointeur vous êtes contraint à:
- accès en lecture seulement pour les membres de données visibles
- permission d'appeler seulement les méthodes qui sont marquées comme
const
.
l'exception mutable
le rend donc vous pouvez maintenant écrire ou définir des membres de données qui sont marqués mutable
. C'est la seule différence visible de l'extérieur.
les méthodes const
qui sont visibles pour vous peuvent aussi écrire aux membres de données qui sont marqués mutable
. Essentiellement, le voile de const est percé complètement. Il est tout à le concepteur de L'API pour s'assurer que mutable
ne détruit pas le concept const
et n'est utilisé que dans des cas spéciaux utiles. Le mot-clé mutable
aide parce qu'il marque clairement les membres de données qui sont soumis à ces cas spéciaux.
dans la pratique, vous pouvez utiliser const
de manière obsessionnelle tout au long de votre codebase (vous voulez essentiellement" infecter "votre codebase avec le const
"maladie"). Dans ce monde pointeurs et références sont const
avec très peu des exceptions, donnant un code plus facile à raisonner et à comprendre. Pour une digression intéressante, consultez "transparence référentielle".
sans le mot-clé mutable
vous serez éventuellement forcé d'utiliser const_cast
pour gérer les différents cas spéciaux utiles qu'il autorise (mise en cache, comptage de ref, débogage de données, etc.). Malheureusement const_cast
est beaucoup plus destructeur que mutable
parce qu'il force L'API client à détruire la protection const
des objets qu'il utilise. En outre, il provoque la destruction généralisée const
: const_cast
ing un pointeur de const ou de référence permet l'écriture et la méthode d'appel non filtré l'accès aux membres visibles. Par contre, mutable
exige que le concepteur de L'API exerce un contrôle minutieux sur les exceptions const
, et ces exceptions sont habituellement cachées dans les méthodes const
qui fonctionnent sur des données privées.
(N.B. I se reporter aux données et à la méthode visibilité à quelques reprises. Je parle de membres marqués comme public par opposition à privé ou protégé qui est un type totalement différent de protection objet discuté ici .)
Votre utilisation avec boost::mutex est exactement ce que ce mot clé est destiné. Une autre utilisation est pour la mise en cache des résultats internes pour accélérer l'accès.
fondamentalement, "mutable" s'applique à tout attribut de classe qui n'affecte pas l'état visible externe de l'objet.
dans le code échantillon de votre question, mutable pourrait être inapproprié si la valeur de done_ affecte l'état externe, cela dépend de ce qui est dans le ...; partie.
Mutables est pour le marquage spécifique de l'attribut modifiable de l'intérieur const
méthodes. C'est son seul but. Réfléchissez bien avant de l'utiliser, car votre code sera probablement plus propre et plus lisible si vous changez le design plutôt que d'utiliser mutable
.
http://www.highprogrammer.com/alan/rants/mutable.html
donc si la folie ci-dessus n'est pas ce que mutable est pour, qu'est-ce que c' pour? Voici le cas subtil: mutable est pour le cas où un objet est logiquement constante, mais dans la pratique, les besoins de changement. Ces cas sont rares et loin entre les deux, mais ils existent.
les exemples que l'auteur donne incluent la mise en cache et les variables de débogage temporaires.
c'est utile dans les situations où vous avez caché l'état interne tel qu'un cache. Par exemple:
class HashTable { ... public: string lookup(string key) const { if(key == lastKey) return lastValue; string value = lookupInternal(key); lastKey = key; lastValue = value; return value; } private: mutable string lastKey, lastValue; };
et alors vous pouvez avoir un objet const HashTable
qui utilise encore sa méthode lookup()
, qui modifie le cache interne.
Eh bien, oui, c'est ce que ça fait. Je l'utilise pour les membres qui sont modifiés par des méthodes qui ne changent pas logiquement l'état d'une classe - par exemple, pour accélérer les recherches en implémentant un cache:
class CIniWrapper
{
public:
CIniWrapper(LPCTSTR szIniFile);
// non-const: logically modifies the state of the object
void SetValue(LPCTSTR szName, LPCTSTR szValue);
// const: does not logically change the object
LPCTSTR GetValue(LPCTSTR szName, LPCTSTR szDefaultValue) const;
// ...
private:
// cache, avoids going to disk when a named value is retrieved multiple times
// does not logically change the public interface, so declared mutable
// so that it can be used by the const GetValue() method
mutable std::map<string, string> m_mapNameToValue;
};
Maintenant, vous devez utiliser cela avec soin - les questions de concurrence sont une grande préoccupation, car un appelant pourrait supposer qu'ils sont thread safe si seulement en utilisant const
méthodes. Et bien sûr, modifier les données mutable
ne devrait pas changer le comportement de l'objet de façon notable, quelque chose qui pourrait être violé par l'exemple que j'ai donné si, par exemple, il était prévu que les modifications écrites sur le disque serait immédiatement visible pour l'application.
mutable
existe comme vous le déduisez pour permettre à quelqu'un de modifier des données dans une fonction par ailleurs constante.
l'intention est que vous pourriez avoir une fonction qui" ne fait rien "à l'état interne de l'objet, et donc vous marquez la fonction const
, mais vous pourriez vraiment avoir besoin de modifier certains de l'état des objets de manière à ne pas affecter sa fonctionnalité correcte.
Le mot-clé peut agir comme indication pour le compilateur -- théorique compilateur pourrait placer un objet constant (une) dans la mémoire qui a été marqué en lecture seule. La présence de mutable
laisse entendre que cela ne devrait pas être fait.
voici quelques raisons valables pour déclarer et utiliser des données mutables:
- Filet de sécurité. Déclarer un
mutable boost::mutex
est parfaitement raisonnable. - des Statistiques. Compter le nombre d'appels à une fonction, étant donné une partie ou la totalité de ses arguments.
- Memoization. Calculer une réponse coûteuse, puis la stocker pour référence future plutôt que de la recalculer à nouveau.
Mutable est utilisé lorsque vous avez une variable à l'intérieur de la classe qui est seulement utilisé dans cette classe pour signaler des choses comme par exemple un mutex ou une serrure. Cette variable ne modifie pas le comportement de la classe, mais est nécessaire pour mettre en œuvre la sécurité des fils de la classe elle-même. Ainsi, si sans "mutable", vous ne seriez pas en mesure d'avoir des fonctions" const " parce que cette variable devra être changée dans toutes les fonctions qui sont disponibles pour le monde extérieur. Par conséquent, mutable était introduit afin de rendre une variable membre inscriptible même par une fonction const.
La mutable spécifié informe le compilateur et le lecteur qu'il est sûr et prévisible qu'une variable de membre peut être modifiée dans un const fonction membre.
mutable est principalement utilisé sur un détail d'implémentation de la classe. L'utilisateur de la classe n'a pas besoin de savoir à ce sujet, donc la méthode il pense "devrait" être const peut être. Votre exemple d'avoir un mutex être mutable est un bon exemple canonique.
votre utilisation de celui-ci n'est pas un hack, bien que comme beaucoup de choses en C++, mutable peut être hack pour un programmeur paresseux qui ne veut pas aller tout le chemin en arrière et marquer quelque chose qui ne devrait pas être const comme non-const.
Utiliser "mutable" lorsque, pour des choses qui sont LOGIQUEMENT apatrides à l'utilisateur (et donc "const" getters dans la classe publique " Api), mais ne sont PAS apatrides dans l'IMPLÉMENTATION sous-jacente (le code dans votre .rpc).
les cas que je l'utilise le plus souvent sont l'initialisation paresseuse de membres" plain old data " sans état. À savoir, il est idéal dans les cas étroits où de tels membres sont coûteux à construire (processeur) ou transporter (mémoire) et de nombreux utilisateurs de l'objet ne vous demandera jamais pour eux. Dans cette situation, vous souhaitez une construction paresseuse à l'arrière pour la performance, puisque 90% des objets construits n'auront jamais besoin de les construire du tout, mais vous avez encore besoin de présenter la bonne API apatride pour la consommation publique.
dans certains cas (comme les itérateurs mal conçus), la classe doit tenir un compte ou une autre valeur incidente, qui n'affecte pas vraiment l '"état" majeur de la classe. C'est le plus souvent là que je vois mutable utilisé. Sans mutable, vous seriez forcé de sacrifier toute la consistance de votre conception.
c'est comme un piratage la plupart du temps pour moi aussi. Utile dans un très petit nombre de situations.
l'exemple classique (comme mentionné dans d'autres réponses) et la seule situation que j'ai vu le mot-clé mutable
utilisé à ce jour, est pour la mise en cache du résultat d'une méthode compliquée Get
, où le cache est mis en œuvre comme un membre de données de la classe et non comme une variable statique dans la méthode (pour des raisons de partage entre plusieurs fonctions ou la propreté pure et simple).
en général, les alternatives à l'utilisation du mot-clé mutable
sont généralement variable dans la méthode ou le tour const_cast
.
une autre explication détaillée se trouve dans ici .
le mot-clé mutable est très utile lors de la création de talons pour des fins de test de classe. Vous pouvez couper une fonction const et tout de même être en mesure d'augmenter (mutable) compteurs ou n'importe quelle fonctionnalité de test que vous avez ajouté à votre talon. Ceci garde l'interface de la classe stubbed intacte.
Mutable change la signification de const
de bitwise const à logical const pour la classe.
cela signifie que les classes avec des membres mutables sont désormais bitwise const et n'apparaîtront plus dans les sections en lecture seule de l'exécutable.
en outre, il modifie le contrôle de type en permettant aux fonctions de membre const
de changer de membre mutable sans utiliser const_cast
.
class Logical {
mutable int var;
public:
Logical(): var(0) {}
void set(int x) const { var = x; }
};
class Bitwise {
int var;
public:
Bitwise(): var(0) {}
void set(int x) const {
const_cast<Bitwise*>(this)->var = x;
}
};
const Logical logical; // Not put in read-only.
const Bitwise bitwise; // Likely put in read-only.
int main(void)
{
logical.set(5); // Well defined.
bitwise.set(5); // Undefined.
}
voir l'autre réponses pour plus de détails, mais je voulais souligner que ce n'est pas simplement pour la sécurité et qu'elle affecte le résultat compilé.
le mutable peut être pratique lorsque vous passez outre une fonction virtuelle const et que vous voulez modifier votre variable child class member dans cette fonction. Dans la plupart des cas, vous ne voudriez pas modifier l'interface de la classe de base, donc vous devez utiliser la variable de membre mutable de votre propre.
Le mot-clé "mutable" est en fait un mot clé réservé.souvent, il est utilisé pour faire varier la valeur de la constante variable.Si vous voulez avoir plusieurs valeurs d'une constsnt,utilisez le mot-clé mutable.
//Prototype
class tag_name{
:
:
mutable var_name;
:
:
};
un des meilleurs exemples où nous utilisons mutable est, en copie profonde. dans copy constructor nous envoyons const &obj
comme argument. Ainsi, le nouvel objet créé sera de type constant. Si nous voulons changer (la plupart du temps nous ne changerons pas, dans de rares cas nous pouvons changer) les membres de cet objet const nouvellement créé, nous devons le déclarer comme mutable
.
mutable
ne peut être utilisée que sur les données non statiques non const d'une classe. Données mutables membre d'une classe peut être modifié même s'il fait partie d'un objet qui est déclaré comme const.
class Test
{
public:
Test(): x(1), y(1) {};
mutable int x;
int y;
};
int main()
{
const Test object;
object.x = 123;
//object.y = 123;
/*
* The above line if uncommented, will create compilation error.
*/
cout<< "X:"<< object.x << ", Y:" << object.y;
return 0;
}
Output:-
X:123, Y:1
dans l'exemple ci-dessus, nous sommes en mesure de changer la valeur de la variable membre x
bien qu'elle fasse partie d'un objet qui est déclaré comme const. C'est parce que la variable x
est déclarée comme mutable. Mais si vous essayez de modifier la valeur de la variable membre y
, le compilateur génère une erreur.