¿Tiene la palabra clave 'mutable' algún otro propósito además de permitir que una función de miembro constante modifique un miembro de datos?
Hace un tiempo, encontré un código que marcaba un miembro de datos de una clase con la mutable
palabra clave. Por lo que puedo ver, simplemente le permite modificar un miembro en un const
método de miembro calificado:
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
as mutable
, permitiendo que const
las funciones lo bloqueen por razones de seguridad de subprocesos, pero, para ser honesto, parece un truco.
Permite la diferenciación de constante bit a bit y constante lógica. La constante lógica es cuando un objeto no cambia de una manera que sea visible a través de la interfaz pública, como en el ejemplo de bloqueo. Otro ejemplo sería una clase que calcula un valor la primera vez que se solicita y almacena en caché el resultado.
Dado que c++11 mutable
se puede usar en una lambda para indicar que las cosas capturadas por valor son modificables (no lo son de forma predeterminada):
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 mutable
palabra clave es una forma de perforar el const
velo que cubres tus objetos. Si tiene una referencia constante o un puntero a un objeto, no puede modificar ese objeto de ninguna manera excepto cuándo y cómo está marcado mutable
.
Con su const
referencia o puntero está obligado a:
- Solo acceso de lectura para cualquier miembro de datos visible.
- permiso para llamar sólo a métodos marcados como
const
.
La mutable
excepción hace que ahora pueda escribir o configurar miembros de datos que estén marcados mutable
. Esa es la única diferencia visible externamente.
Internamente, aquellos const
métodos que son visibles para usted también pueden escribir en miembros de datos que están marcados mutable
. Básicamente, el velo constante se perfora por completo. Depende completamente del diseñador de API asegurarse de que mutable
no destruya el const
concepto y solo se use en casos especiales útiles. La mutable
palabra clave ayuda porque marca claramente los miembros de datos que están sujetos a estos casos especiales.
En la práctica, puedes utilizarlo const
obsesivamente en todo tu código base (esencialmente quieres "infectar" tu código base con la const
"enfermedad"). En este mundo, los punteros y las referencias, const
con muy pocas excepciones, producen un código que es más fácil de razonar y comprender. Para una digresión interesante, consulte "transparencia referencial".
Sin la mutable
palabra clave, eventualmente se verá obligado a utilizarla const_cast
para manejar los diversos casos especiales útiles que permite (almacenamiento en caché, recuento de referencias, datos de depuración, etc.). Desafortunadamente const_cast
, es significativamente más destructivo que mutable
porque obliga al cliente API a destruir la const
protección de los objetos que está utilizando. Además, causa const
una destrucción generalizada: const_cast
la creación de un puntero o referencia constante permite acceso sin restricciones a la escritura y a las llamadas a métodos a los miembros visibles. Por el contrario, mutable
requiere que el diseñador de API ejerza un control detallado sobre las const
excepciones y, por lo general, estas excepciones están ocultas en const
métodos que operan con datos privados.
(NB: me refiero a la visibilidad de datos y métodos varias veces. Me refiero a miembros marcados como públicos versus privados o protegidos, que es un tipo totalmente diferente de protección de objetos que se analiza aquí ).
Su uso con boost::mutex es exactamente para lo que está destinada esta palabra clave. Otro uso es el almacenamiento en caché interno de resultados para acelerar el acceso.
Básicamente, "mutable" se aplica a cualquier atributo de clase que no afecte el estado visible externamente del objeto.
En el código de muestra de su pregunta, mutable puede ser inapropiado si el valor de done_ afecta el estado externo, depende de lo que esté en ...; parte.