2009-11-25 21 views
9

Tengo un miembro de clase myMember que es un myType puntero. Quiero asignar este miembro en una función que se declara como const. Estoy haciendo la siguiente manera:Asignación de miembro en una función const

void func() const 
{ 
    ... 
    const_cast<myType*>(myMember) = new myType(); 
    ... 
} 

Hacer esto funciona bien en VC++, pero GCC da un error con el "valor-I requerido como operador de la izquierda de la asignación" mensaje.

Haciendo el miembro mutable permítanme simplemente eliminar el const_cast y asignar el valor. Sin embargo, no estoy del todo seguro de que eso tenga otros efectos secundarios.

¿Puedo asignar mi miembro sin tener que hacer que el miembro sea mutable? ¿Cómo? ¿Hay algún efecto secundario en hacer mutable a los miembros?

Respuesta

8

El código de costumbre en realidad el trabajo en VC++ - no se está actualizando el valor (o al menos que no deberia), de ahí la advertencia de GCC. código correcto es

const_cast<myType*&>(myMember) = new myType(); 

o [de otra respuesta, gracias: P]:

const_cast<ThisType*>(this)->myMember = new myType(); 

Por lo que es mutable efectivamente significa que usted obtiene implícitas const_cast s en const funciones miembro, que generalmente es lo que debe ser dirigiéndose hacia cuando te encuentras haciendo un montón de const_cast s en this. No hay 'efectos colaterales al uso de' mutable 'que no sea eso.

Como puede ver en los vehemente debates que rodean esta pregunta, el uso indebido de mutable y muchos const_cast s definitivamente pueden ser síntomas de malos olores en su código. Desde un punto de vista conceptual, la eliminación de la constness o el uso de mutable puede tener implicaciones mucho más grandes. En algunos casos, lo correcto puede ser cambiar el método a no const, es decir, admitir el hecho de que está modificando el estado.

Todo depende de la cantidad de const-corrección cuestiones en su contexto - usted no quiere terminar simplemente sprinking mutable alrededor como polvo mágico para hacer funcionar las cosas, pero mutable está destinado para el uso si la pieza miembro de isnt de lo observable estado del objeto. La visión más rigurosa de la corrección de constión sostendría que no se puede modificar ni un solo bit del estado del objeto (p. Ej., Esto podría ser crítico si su instancia está en ROM ...) - en esos casos no quiere ninguna constness estar perdido. En otros casos, puede tener algún estado externo almacenado en algún lugar fuera del objeto; por ejemplo, un caché específico de subprocesos que también debe tenerse en cuenta al decidir si es apropiado.

+0

Gracias. [min 15 caracteres] –

+1

Creo que mientras esto funciona es posiblemente la peor solución. Oculta completamente lo que ocurre (cambiando un estado interno de una instancia inmutable) en un molde de fusión en la función miembro. "mutable" sería más claro (o usando interfaces). –

+1

fundir constness conduce a un comportamiento indefinido. Úselo bajo su propio riesgo. Una mejor solución sería hacer que el método no const. –

17

Este escenario - un cambio de estado interno encapsulado que no afecta el estado externo (por ejemplo, resultados de almacenamiento en caché) - es exactamente lo que significa la palabra clave mutable.

+1

Mutable no es una buena forma si desea obtener acceso de escritura al miembro en un solo método const. – Staseg

+4

El estado externamente observable del objeto no debe cambiar cuando se escribe en un objeto mutable. Hacer que un miembro sea mutable solo para eludir un error en una función const no es legítimo si el cambio es visible externamente y puede llevar a un comportamiento indefinido. Los usos legítimos de mutable incluyen el almacenamiento en caché, donde, por ejemplo, la misma llamada dos veces devuelve el mismo resultado, pero más rápido. – AshleysBrain

5
class Class{ 
int value; 
void func()const{ 
const_cast<Class*>(this)->value=123; 
} 
}; 
+0

Necesita lanzar el objeto en lugar de un miembro. – Staseg

+0

Gracias. Voy por el primer código de Ruben ya que se ve más limpio. Si pudiera, marcaría ambos como "respuesta". –

6

const_cast es casi siempre un signo de error de diseño. En su ejemplo, func() no debe ser const, o myMember debe ser mutable.

Una persona que llama de func() esperará que su objeto no cambie; pero esto significa "no cambiar de una manera que ella pueda notar"; esto es, no para cambiar su estado externo.Si al cambiar myMember no se cambia el estado externo del objeto, para eso es la palabra clave mutable; de lo contrario, func() no debe ser const, porque traicionaría las garantías de su función.

Recuerde que mutable no es un mecanismo para circunvenir la const-correctness; es un mecanismo para mejorarlo.

+1

+1 "const_cast es casi siempre un signo de falla de diseño" –

+0

^¡Sí, y a veces la falla está en una biblioteca que está usando! Utilizo uno que toma un puntero-a-no-const' pero (y lo compruebo regularmente :) no muta el objeto. Asegure en este conocimiento, para que pueda pasar cualquier 'Obj const &' (incluido un temporal) a dicha biblioteca, lo envuelvo en una función que hace un 'const_cast (& constObj)'. Feo, tal vez, pero más cierto que la realidad de lo que realmente sucede con el 'Obj' y me permite hacer cosas mejores con la biblioteca ... al menos hasta que el autor decida usar C++ más que C [suspiro] –

+0

@underscore_d Esa es una de los usos legítimos de 'const_cast': tratar con C++ imperfecto. Si tuviera un centavo por cada ponter-to-no-const que debería ser un puntero-a-const en el mundo ... – Gorpik

1

Como escribió Steve Gilham, mutable es la respuesta correcta (y breve) a su pregunta. Solo quiero darte una pista en una dirección diferente. Tal vez es posible en su szenario hacer uso de una (o más de) interfaz? Tal vez se puede asimilar desde el siguiente ejemplo:

class IRestrictedWriter // may change only some members 
{ 
public: 
    virtual void func() = 0; 
} 

class MyClass : virtual public IRestrictedWriter 
{ 
public: 
    virtual void func() 
    { 
    mValueToBeWrittenFromEverybody = 123; 
    } 

    void otherFunctionNotAccessibleViaIRestrictedWriter() 
    { 
    mOtherValue1 = 123; 
    mOtherValue2 = 345; 
    } 

    ... 
} 

Por lo tanto, si pasa a una cierta función IRestrictedReader * en lugar de un const MyClass * puede llamar func y así cambiar mValueToBeWrittenFromEverybody mientras que mOtherValue1 es una especie de "const".

. Encuentro que mutable siempre es un hack (pero úsala a veces).

Cuestiones relacionadas