¿La palabra clave “mutable” tiene algún otro propósito que no sea permitir que una función const modifique la variable?

Hace un tiempo encontré un código que marcaba una variable miembro de una clase con la palabra clave mutable . Por lo que puedo ver, simplemente te permite modificar una variable en un método const :

 class Foo { private: mutable bool done_; public: void doSomething() const { ...; done_ = true; } }; 

¿Es este el único uso de esta palabra clave o hay más de lo que parece? Desde entonces, he usado esta técnica en una clase, marcando un boost::mutex como mutable que permite a las funciones de const bloquearlo por razones de seguridad de hilos, pero, para ser honesto, se siente como un hack.

Permite la diferenciación de const bitwise y const lógico. Const lógico es cuando un objeto no cambia de una manera visible a través de la interfaz pública, como su ejemplo de locking. Otro ejemplo sería una clase que calcula un valor la primera vez que se solicita y almacena el resultado en caché.

Dado que c ++ 11 mutable se puede utilizar en una lambda para indicar que las cosas capturadas por el valor son modificables (no son por defecto):

 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 

La palabra clave mutable es una forma de perforar el velo const que cubre sus objetos. Si tiene una referencia constante o un puntero a un objeto, no puede modificar ese objeto de ninguna manera, excepto cuando y cómo se marca mutable .

Con su referencia o puntero const usted está obligado a:

  • solo acceso de lectura para cualquier miembro de datos visible
  • permiso para llamar solo a los métodos que están marcados como const .

La excepción mutable hace para que ahora pueda escribir o establecer miembros de datos marcados como mutable . Esa es la única diferencia externamente visible.

Internamente, los métodos const que son visibles para usted también pueden escribir a los miembros de datos que están marcados como mutable . Esencialmente, el velo de const se perfora comprensivamente. El diseñador de la API debe asegurarse de que mutable no destruya el concepto de const y solo se use en casos especiales útiles. La palabra clave mutable ayuda porque marca claramente a los miembros de los datos que están sujetos a estos casos especiales.

En la práctica, puede utilizar const obsesivamente a lo largo de su base de código (esencialmente desea “infectar” su base de código con la const “enfermedad”). En este mundo, los punteros y las referencias son const con muy pocas excepciones, produciendo un código que es más fácil de razonar y comprender. Para una digresión interesante, busque “transparencia referencial”.

Sin la palabra clave mutable , eventualmente se verá obligado a usar const_cast para manejar los diversos casos especiales útiles que permite (almacenamiento en caché, recuento de ref, datos de depuración, etc.). Desafortunadamente, const_cast es significativamente más destructivo que mutable porque obliga al cliente API a destruir la protección const de los objetos que está utilizando. Además, causa una destrucción generalizada: la const_cast de un puntero o referencia const permite la escritura sin restricciones y el acceso a métodos de llamada a miembros visibles. Por el contrario, mutable requiere que el diseñador de API ejerza un control detallado sobre las excepciones const , y generalmente estas excepciones están ocultas en los métodos const que operan en datos privados.

(NB Me refiero a la visibilidad de los datos y métodos algunas veces. Estoy hablando de miembros marcados como públicos versus privados o protegidos, que es un tipo de protección de objetos totalmente diferente que se discute aquí ).

Su uso con boost :: mutex es exactamente para lo que está diseñada esta palabra clave. Otro uso es para el almacenamiento en memoria caché de resultados internos para acelerar el acceso.

Básicamente, ‘mutable’ se aplica a cualquier atributo de clase que no afecte el estado externo visible del objeto.

En el código de muestra de su pregunta, mutable podría ser inapropiado si el valor de done_ afecta el estado externo, depende de lo que está en …; parte.

Mutable es para marcar un atributo específico como modificable desde métodos const . Ese es su único propósito. Piense con cuidado antes de usarlo, ya que su código probablemente será más limpio y más legible si cambia el diseño en lugar de usar mutable .

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

Entonces, si la locura anterior no es para qué es mutable, ¿para qué sirve? Aquí está el caso sutil: mutable es para el caso donde un objeto es lógicamente constante, pero en la práctica necesita cambiar. Estos casos son pocos y distantes, pero existen.

Ejemplos que el autor proporciona incluyen caché y variables de depuración temporal.

Es útil en situaciones donde tiene un estado interno oculto, como un caché. Por ejemplo:

 clase HashTable
 {
 ...
 público:
     búsqueda de cadena (clave de cadena) const
     {
         if (clave == lastKey)
             devolver lastValue;

         string value = lookupInternal (clave);

         lastKey = tecla;
         lastValue = valor;

         valor de retorno;
     }

 privado:
     cadena mutable lastKey, lastValue;
 }; 

Y luego puede hacer que un objeto const HashTable siga usando su método lookup() , que modifica el caché interno.

Bueno, sí, eso es lo que hace. Lo uso para miembros que son modificados por métodos que lógicamente no cambian el estado de una clase, por ejemplo, para acelerar las búsquedas implementando un caché:

 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 m_mapNameToValue; }; 

Ahora, debe usar esto con cuidado: los problemas de concurrencia son una gran preocupación, ya que una persona que llama puede asumir que son seguros para subprocesos si solo usa métodos const . Y, por supuesto, la modificación de datos mutable no debería cambiar el comportamiento del objeto de manera significativa, algo que podría ser violado por el ejemplo que dí si, por ejemplo, se esperara que los cambios escritos en el disco fueran inmediatamente visibles para el aplicación

mutable existe como se infiere para permitir que uno modifique los datos en una función constante.

La intención es que pueda tener una función que “no haga nada” para el estado interno del objeto, por lo que marca la función const , pero es posible que necesite modificar algunos de los objetos de forma que no afecte su estado. funcionalidad correcta

La palabra clave puede actuar como una sugerencia para el comstackdor: un comstackdor teórico podría colocar un objeto constante (como un global) en la memoria que estaba marcada como de solo lectura. La presencia de sugerencias mutable que esto no debería hacerse.

Aquí hay algunas razones válidas para declarar y usar datos mutables:

  • Hilo de seguridad. Declarar un mutable boost::mutex es perfectamente razonable.
  • Estadística. Contando el número de llamadas a una función, dado algunos o todos sus argumentos.
  • Memoización. Computando una respuesta costosa, y luego almacenándola para referencia futura en lugar de volver a calcularla nuevamente.

mutable se usa principalmente en un detalle de implementación de la clase. El usuario de la clase no necesita saber al respecto, por lo tanto, el método que él cree que “debería” ser const puede ser. Tu ejemplo de tener un mutex mutable es un buen ejemplo canónico.

Su uso no es un truco, aunque como muchas cosas en C ++, mutable puede ser pirateado para un progtwigdor perezoso que no quiere volver atrás y marcar algo que no debería ser const como non-const.

Mutable se usa cuando tiene una variable dentro de la clase que solo se usa dentro de esa clase para señalar cosas como, por ejemplo, un mutex o un locking. Esta variable no cambia el comportamiento de la clase, pero es necesaria para implementar la seguridad de subprocesos de la clase misma. Por lo tanto, si no se puede “mutar”, no se podrían tener funciones “const” porque esta variable deberá cambiarse en todas las funciones disponibles para el mundo exterior. Por lo tanto, se introdujo mutable para hacer que una variable miembro pueda escribirse incluso mediante una función const.

El parámetro mutable especificado informa tanto al comstackdor como al lector de que es seguro y se espera que una variable miembro se pueda modificar dentro de una función miembro miembro.

Utilice “mutable” para las cosas que son LOGICAMENTE apátridas para el usuario (y, por lo tanto, deberían tener getters “const” en las API de clase pública) pero NO son apátridas en la IMPLEMENTACIÓN subyacente (el código en su .cpp).

Los casos que uso con mayor frecuencia son la inicialización lenta de miembros “sin datos antiguos” sin estado. A saber, es ideal en los casos limitados cuando dichos miembros son costosos para construir (procesador) o para transportar (memoria) y muchos usuarios del objeto nunca los solicitarán. En esa situación, usted quiere una construcción perezosa en la parte de atrás para el rendimiento, ya que el 90% de los objetos creados nunca necesitará construirlos en absoluto, sin embargo, todavía necesita presentar la API apátrida correcta para el consumo público.

En algunos casos (como los iteradores mal diseñados), la clase necesita mantener un recuento o algún otro valor incidental, que realmente no afecta el “estado” principal de la clase. Esto es más a menudo donde veo mutable utilizado. Sin mutable, se vería obligado a sacrificar toda la consistencia de su diseño.

Se siente como un truco la mayor parte del tiempo para mí también. Útil en muy pocas situaciones.

El ejemplo clásico (como se menciona en otras respuestas) y la única situación en la que he visto la palabra clave mutable utilizada hasta ahora, es almacenar en caché el resultado de un método Get complicado, donde la caché se implementa como un miembro de datos de la clase y no como una variable estática en el método (por razones de compartir entre varias funciones o limpieza simple).

En general, las alternativas al uso de la palabra clave mutable suelen ser una variable estática en el método o el truco de const_cast .

Otra explicación detallada está aquí .

La palabra clave mutable es muy útil al crear stubs para fines de prueba de clase. Puede poner una función const y aún así poder boost los contadores (mutables) o la funcionalidad de prueba que haya agregado a su stub. Esto mantiene intacta la interfaz de la clase apagada.

Mutable cambia el significado de const de const bitwise a const lógico para la clase.

Esto significa que las clases con miembros mutables son más largas const con bit y ya no aparecerán en las secciones de solo lectura del ejecutable.

Además, modifica la verificación de tipo al permitir que las funciones de miembros de la serie cambien miembros mutables sin usar 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(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. } 

Vea las otras respuestas para más detalles, pero quería resaltar que no se trata solo de seguridad de tipo y que afecta el resultado comstackdo.

El mutable puede ser útil cuando reemplaza una función virtual const y desea modificar su variable miembro de clase secundaria en esa función. En la mayoría de los casos, no le conviene alterar la interfaz de la clase base, por lo que debe usar su propia variable mutable.

La misma palabra clave ‘mutable’ es en realidad una palabra clave reservada. A menudo se usa para variar el valor de la variable constante. Si desea tener múltiples valores de un constsnt, use la palabra clave mutable.

 //Prototype class tag_name{ : : mutable var_name; : : }; 

Uno de los mejores ejemplos donde usamos mutable es, en copia profunda. en copy constructor enviamos const &obj como argumento. Entonces el nuevo objeto creado será de tipo constante. Si queremos cambiar (la mayoría no cambiaremos, en algunos casos podemos cambiar) los miembros en este objeto const recién creado, debemos declararlo como mutable .

mutable clase de almacenamiento mutable se puede usar solo en miembros de una clase no estáticos no const. El miembro de datos mutable de una clase se puede modificar incluso si es parte de un objeto que se declara como 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 comstacktion error. */ cout< < "X:"<< object.x << ", Y:" << object.y; return 0; } Output:- X:123, Y:1 

En el ejemplo anterior, podemos cambiar el valor de la variable miembro x aunque es parte de un objeto que se declara como const. Esto se debe a que la variable x se declara como mutable. Pero si intenta modificar el valor de la variable miembro y , el comstackdor emitirá un error.