Question Le mot clé 'mutable' a-t-il un but autre que de permettre à la variable d'être modifiée par une fonction const?


Il y a quelque temps, je suis tombé sur un code qui marquait une variable membre d'une classe avec le code mutable mot-clé. Pour autant que je puisse le voir, il vous suffit de modifier une variable dans un 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 y a-t-il plus que ce que l'on voit? J'ai depuis utilisé cette technique dans une classe, marquant un boost::mutex comme permutable const fonctions pour le verrouiller pour des raisons de sécurité de thread, mais, pour être honnête, cela ressemble à un peu un hack.


469
2017-09-19 19:58


origine


Réponses:


Il permet la différenciation des const constants et des constants binaires. La constante logique est lorsqu'un objet ne change pas de manière visible via l'interface publique, comme votre exemple de verrouillage. Un autre exemple serait une classe qui calcule une valeur la première fois qu'elle est demandée et met en cache le résultat.

Depuis c ++ 11 mutable peut être utilisé sur un lambda pour indiquer que les objets capturés par valeur sont modifiables (ils ne le 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

312
2017-09-19 20:04



le mutable mot-clé est un moyen de percer le const voile vous drape sur vos objets. Si vous avez une référence ou un pointeur const sur 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 de:

  • Accès en lecture uniquement pour les membres de données visibles
  • l'autorisation d'appeler uniquement les méthodes marquées comme const.

le mutable exception fait en sorte que vous pouvez maintenant écrire ou définir des membres de données qui sont marqués mutable. C'est la seule différence visible à l'extérieur.

En interne ceux const les méthodes qui vous sont visibles peuvent également écrire aux membres de données marqués mutable. Essentiellement le voile de const est percé complètement. Il incombe entièrement au concepteur d'API de s'assurer que mutable ne détruit pas le const concept et n'est utilisé que dans des cas particuliers utiles. le mutable mot-clé aide car il marque clairement les membres de données qui sont soumis à ces cas particuliers.

En pratique, vous pouvez utiliser const de manière obsessionnelle dans votre base de code (vous voulez essentiellement "infecter" votre base de code avec le const "maladie"). Dans ce monde, les pointeurs et les références sont const avec très peu d'exceptions, donnant un code qui est plus facile à raisonner et à comprendre. Pour une digression intéressante, recherchez "transparence référentielle".

Sans le mutable mot-clé que vous serez éventuellement obligé d'utiliser const_cast pour gérer les différents cas particuliers utiles qu’il permet (mise en cache, comptage de références, données de débogage, etc.). Malheureusement const_cast est significativement plus destructrice que mutable parce qu'il force l'API client détruire le const la protection des objets qu'il utilise. En outre, il provoque une large diffusion const destruction: const_castun pointeur ou une référence const permet une écriture sans entrave et une méthode appelant l'accès aux membres visibles. En revanche mutableexige que le concepteur de l'API exerce un contrôle fin sur la const exceptions, et généralement ces exceptions sont cachées dans const méthodes opérant sur des données privées.

(NB: je me réfère aux données et à la méthode visibilité parfois. Je parle de membres marqués comme public vs privé ou protégé qui est un type totalement différent de la protection de l'objet discuté ici.)


117
2018-03-05 02:34



Votre utilisation avec boost :: mutex est exactement ce à quoi ce mot-clé est destiné. Une autre utilisation est la mise en cache interne des résultats pour accélérer l'accès.

Fondamentalement, "mutable" s'applique à tout attribut de classe n'affectant pas l'état visible de l'objet en externe.

Dans l'exemple de code de votre question, mutable peut être inapproprié si la valeur de done_ affecte l'état externe, cela dépend de ce qui est dans le ...; partie.


74
2017-09-19 20:02



Mutable permet de marquer des attributs spécifiques comme modifiables depuis 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 modifiez la conception plutôt que de l'utiliser. mutable.

http://www.highprogrammer.com/alan/rants/mutable.html

Donc, si la folie ci-dessus n'est pas ce que   mutable est pour, à quoi sert-il? Voici   le cas subtil: mutable est pour le   cas où un objet est logiquement   constante, mais en pratique doit   changement. Ces cas sont rares et éloignés   entre, mais ils existent.

Les exemples que l'auteur donne incluent la mise en cache et les variables de débogage temporaires.


35
2017-09-19 20:03



Il est utile dans les situations où vous avez un état interne caché tel qu'un cache. Par exemple:

classe HashTable
{
...
Publique:
    recherche de chaîne (clé de chaîne) const
    {
        if (clé == lastKey)
            retourne la dernière valeur;

        chaîne valeur = lookupInternal (clé);

        lastKey = clé;
        lastValue = valeur;

        valeur de retour;
    }

privé:
    chaîne mutable lastKey, lastValue;
}

Et puis vous pouvez avoir un const HashTable objet utilise toujours son lookup() méthode, qui modifie le cache interne.


29
2017-09-19 20:07



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 le font pas logiquement changer 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 précaution - les problèmes de concurrence sont un gros problème, car un appelant peut supposer qu'ils sont thread-safe en utilisant seulement const méthodes Et bien sûr, en modifiant mutable les données ne devraient pas modifier le comportement de l'objet de manière significative, ce qui pourrait être violé par l'exemple que je donnais si, par exemple, il était prévu que les modifications écrites sur le disque seraient immédiatement visibles par l'application.


8
2017-09-19 20:25



mutable existe comme vous en déduisez pour permettre de modifier les données dans une fonction par ailleurs constante.

L'intention est que vous puissiez avoir une fonction qui "ne fait rien" à l'état interne de l'objet, et donc vous marquez la fonction const, mais vous devrez peut-être modifier certains des états des objets de manière à ne pas affecter la fonctionnalité correcte.

Le mot-clé peut servir d'indicateur au compilateur - un compilateur théorique peut placer un objet constant (tel qu'un global) en mémoire marqué en lecture seule. La présence de mutable des indices que cela ne devrait pas être fait.

Voici quelques raisons valables de déclarer et d'utiliser des données mutables:

  • Sécurité des fils. Déclarer un mutable boost::mutex est parfaitement raisonnable.
  • Statistiques. Compter le nombre d'appels à une fonction, compte tenu de tout ou partie de ses arguments.
  • Mémo. Calculer une réponse coûteuse, puis la stocker pour référence future plutôt que de la recalculer à nouveau.

8
2017-09-19 20:05



mutable est principalement utilisé sur un détail d'implémentation de la classe. L'utilisateur de la classe n'a pas besoin de le savoir, par conséquent les méthodes qu'il pense "devraient" être constantes. Votre exemple d'un mutex mutable est un bon exemple canonique.


4
2017-09-19 20:20



Votre utilisation de ce n'est pas un hack, mais comme beaucoup de choses en C ++, mutable pouvez être un hack pour un programmeur paresseux qui ne veut pas revenir en arrière et marquer quelque chose qui ne devrait pas être const comme non-const.


4
2018-06-28 12:31