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.

472
demandé sur M4N 2008-09-19 23:58:05

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
318
répondu KeithB 2013-04-06 14:10:53

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 .)

119
répondu Dan L 2017-05-23 12:34:47

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.

74
répondu Frank Szczerba 2008-09-19 20:01:20

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.

35
répondu John Millikin 2008-09-19 20:02:55

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.

29
répondu Adam Rosenfield 2008-09-19 20:03:13

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.

8
répondu Shog9 2008-09-19 20:07:14

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.
8
répondu Lloyd 2008-09-19 20:25:03

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.

5
répondu mkschreder 2013-06-28 12:31:09

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.

4
répondu Greg Rogers 2008-09-19 20:05:22

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.

4
répondu JohnMcG 2008-09-19 20:20:49

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.

3
répondu Zack Yezek 2014-01-14 02:38:37

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.

1
répondu Joe Schneider 2008-09-19 20:03:04

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 .

1
répondu Daniel Hershcovich 2011-05-16 12:32:31

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.

1
répondu Martin G 2014-05-27 08:06:05

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é.

1
répondu Kevin Cox 2014-07-24 19:50:19

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.

0
répondu Saurabh 2014-02-01 14:15:16

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;
                :
                :
               };   
0
répondu Rajdeep Rathore 2014-02-22 03:42:45

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 .

La classe de stockage

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.

0
répondu Venkatakrishna Kalepalli 2015-04-16 12:09:27