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

StackOverflow https://stackoverflow.com/questions/105014

  •  01-07-2019
  •  | 
  •  

Question

Il y a quelque temps, je suis tombé sur un code marquant une variable membre d'une classe avec le mot clé mutable . Autant que je sache, cela vous permet simplement de modifier une variable dans une méthode const :

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 autre chose que ce que l'on pourrait voir? J'ai depuis utilisé cette technique dans une classe, marquant un boost :: mutex comme étant mutable, permettant ainsi à des fonctions const de la verrouiller pour des raisons de sécurité du thread, mais, pour être honnête, cela ressemble à un peu de bidouille.

Était-ce utile?

La solution

Il permet la différenciation de const au niveau de bit et de const logique. La constante logique se produit lorsqu'un objet ne change pas d'une manière visible via l'interface publique, comme dans l'exemple de verrouillage. Un autre exemple serait une classe qui calcule une valeur lors de la première demande et met en cache le résultat.

Puisque c ++ 11 mutable peut être utilisé sur un lambda pour indiquer que les éléments capturés par la 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

Autres conseils

Le mot clé mutable permet de percer le voile const que vous drapez sur vos objets. Si vous avez une référence const ou un pointeur sur un objet, vous ne pouvez en aucune manière modifier cet objet sauf quand et comment il est marqué mutable .

Avec votre référence ou votre pointeur const , vous êtes contraint de:

  • accès en lecture uniquement aux membres de données visibles
  • permission d'appeler uniquement les méthodes marquées comme const .

L’exception mutable permet d’écrire ou de définir des membres de données marqués mutable . C’est la seule différence visible de l’extérieur.

En interne, les méthodes const visibles par vous peuvent également écrire sur les membres de données marqués mutable . Essentiellement, le voile de const est complètement percé. Il appartient au concepteur de l’API de 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 est utile car il marque clairement les membres de données soumis à ces cas particuliers.

En pratique, vous pouvez utiliser const de manière obsessionnelle tout au long de 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 , à de très rares exceptions près, donnant un code plus facile à raisonner et à comprendre. Pour une digression intéressante, recherchez "transparence référentielle".

Sans le mot clé mutable , vous serez éventuellement obligé d'utiliser const_cast pour gérer les divers cas spéciaux utiles qu'il permet (mise en cache, comptage des références, données de débogage, etc.). . Malheureusement, const_cast est beaucoup plus destructeur que mutable car il force l'API client à détruire la protection const des objets. (s) il utilise. En outre, cela provoque une destruction généralisée const : const_cast un pointeur ou une référence const permet un accès en écriture et en méthode sans entrave aux membres visibles. En revanche, mutable oblige le concepteur d’API à exercer un contrôle fin sur les exceptions const , et ces exceptions sont généralement masquées dans des méthodes const fonctionnant en mode privé. données.

(Remarque: je me réfère aux données et à la méthode Visibilité à quelques reprises. Je parle de membres marqués comme publics ou privés ou protégés, ce qui est un type de protection d'objet totalement différent, présenté ici .)

Votre utilisation avec boost :: mutex correspond 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 de l'extérieur.

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

Mutable permet de marquer un attribut spécifique comme étant modifiable depuis les méthodes const . 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 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, à quoi ça sert? Voici   le cas subtil: mutable est pour le   cas où un objet est logiquement   constante, mais en pratique doit   changement. Ces cas sont rares   entre, mais ils existent.

Parmi les exemples cités par l'auteur, citons la mise en cache et les variables de débogage temporaires.

C'est utile dans les situations où vous avez un état interne caché 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;
};

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

Eh bien, oui, c'est ce que ça fait. Je l'utilise pour les membres modifiés par des méthodes qui ne logiquement ne modifient pas 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 ceci avec précaution - les problèmes de simultanéité sont un gros problème, car un appelant peut supposer qu'ils sont thread-safe s'ils n'utilisent que des méthodes const . Et bien sûr, la modification de données mutable ne devrait pas modifier le comportement de l'objet de manière significative, ce qui pourrait être violé par l'exemple que j'ai donné si, par exemple, il était prévu que les modifications écrites dans le disque serait immédiatement visible pour l'application.

mutable existe comme vous le permettez pour permettre à un utilisateur de modifier des données dans une fonction par ailleurs constante.

L'intention est que vous puissiez avoir une fonction qui "ne fait rien". vous marquez donc la fonction const , mais vous devrez peut-être vraiment modifier certains états de l'objet sans modifier sa fonctionnalité.

Le mot clé peut être un indice pour le compilateur - un compilateur théorique pourrait placer un objet constant (tel qu'un objet global) en 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:

  • Sécurité du fil. Déclarer un mutable boost :: mutex est parfaitement raisonnable.
  • Statistiques. Compter le nombre d’appels à une fonction, en fonction de tout ou partie de ses arguments.
  • Mémorisation. Calculez une réponse coûteuse, puis stockez-la pour référence future plutôt que de la recalculer.

Mutable est utilisé lorsque vous avez une variable dans la classe qui est utilisée uniquement dans cette classe pour signaler des choses comme par exemple un mutex ou un verrou. Cette variable ne modifie pas le comportement de la classe, mais est nécessaire pour implémenter la sécurité des threads de la classe elle-même. Ainsi, sans "mutable", vous ne pourriez pas avoir "const". fonctions car cette variable devra être modifiée dans toutes les fonctions disponibles pour le monde extérieur. Par conséquent, mutable a été introduit afin de rendre une variable membre accessible en écriture, même par une fonction const.

  

Le mutable spécifié informe à la fois le compilateur et le lecteur qu'il   est sûr et s'attend à ce qu'une variable membre puisse ê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 le savoir, il pense donc que la méthode "devrait". être const peut être. Votre exemple d’avoir un mutex mutable est un bon exemple canonique.

Votre utilisation n’est pas un hack, même si, comme beaucoup de choses en C ++, modifiable peut ê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.

Utilisez "mutable". when pour des choses qui sont logiquement sans état pour l'utilisateur (et doivent donc avoir des "constants" dans les API de la classe publique) mais qui ne sont PAS sans état dans l'IMPLLEMENTATION sous-jacente (le code dans votre .cpp).

Les cas que je l'utilise le plus souvent sont l'initialisation paresseuse de "données anciennes lisses" sans état. membres. À savoir, il est idéal dans les cas les plus restreints lorsque ces membres sont coûteux à construire (processeur) ou à transporter (mémoire) et que de nombreux utilisateurs de l’objet ne les demanderont jamais. Dans cette situation, vous souhaitez des performances ludiques sur le back-end, car 90% des objets construits n'auront jamais besoin de les construire, mais vous devez néanmoins présenter la bonne API sans état pour la consommation publique.

Mutable change le sens de const de bitwise const en logique logique pour la classe.

Cela signifie que les classes avec des membres mutables sont plus longues et restent constantes au niveau du bit et qu'elles n'apparaîtront plus dans les sections en lecture seule de l'exécutable.

De plus, il modifie la vérification de type en permettant aux fonctions de membre const de modifier les membres mutables 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 les autres réponses pour plus de détails, mais je voulais souligner que ce n'est pas simplement pour la sécurité du type et que cela affecte le résultat compilé.

Dans certains cas (comme des itérateurs mal conçus), la classe doit garder un compte ou une autre valeur incidente qui n’affectera pas vraiment le principal "état". de la classe. C'est le plus souvent où je vois mutable utilisé. Sans mutable, vous seriez obligé de sacrifier l’intégralité de votre conception.

Pour moi aussi, ça ressemble à un bidouillage. Utile dans très très peu de situations.

L'exemple classique (comme mentionné dans d'autres réponses) et la seule situation dans laquelle j'ai vu le mot clé mutable utilisé jusqu'ici est destiné à la mise en cache du résultat d'un Get méthode, où le cache est implémenté en tant que membre de données de la classe et non en tant que variable statique dans la méthode (pour des raisons de partage entre plusieurs fonctions ou de propreté absolue).

En général, les alternatives à l'utilisation du mot clé mutable sont généralement une variable statique dans la méthode ou l'astuce const_cast .

Vous trouverez une autre explication détaillée dans ici .

Le mutable peut être pratique lorsque vous redéfinissez une fonction virtuelle const et que vous souhaitez modifier votre variable membre de classe enfant dans cette fonction. Dans la plupart des cas, vous ne voudriez pas modifier l'interface de la classe de base, vous devez donc utiliser votre propre variable membre mutable.

Le mot clé mutable est très utile lors de la création de stubs à des fins de test de classe. Vous pouvez remplacer une fonction const tout en augmentant le nombre de compteurs (modifiables) ou la fonctionnalité de test que vous avez ajoutée à votre talon. Ceci conserve l’interface de la classe stubbed intacte.

L’un des meilleurs exemples d’utilisation de mutable est la copie en profondeur. dans le constructeur de copie, nous envoyons const & amp; obj en tant qu'argument. Le nouvel objet créé sera donc de type constant. Si nous voulons changer (nous ne changerons généralement pas, mais dans de rares cas, nous pouvons changer) les membres de cet objet const nouvellement créé, nous devons le déclarer comme mutable .

La classe de stockage

mutable ne peut être utilisée que sur un membre de données non statique non const d'une classe. Le membre de données mutable d'une classe peut être modifié même s'il fait partie d'un objet 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 pouvons modifier la valeur de la variable membre x bien qu'elle fasse partie d'un objet déclaré en tant que const. En effet, la variable x est déclarée mutable. Mais si vous essayez de modifier la valeur de la variable membre y , le compilateur générera une erreur.

Le mot-clé 'mutable' est en fait un mot-clé réservé. Il sert souvent à faire varier la valeur de la variable constante. Si vous souhaitez avoir plusieurs valeurs d'un constsnt, utilisez le mot-clé mutable.

//Prototype 
class tag_name{
                :
                :
                mutable var_name;
                :
                :
               };   
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top