Pregunta

Necesito llamar a una función const desde un objeto no const. Ver ejemplo

struct IProcess {
   virtual bool doSomeWork() const = 0L;
};
class Foo : public IProcess {    
  virtual bool doSomeWork() const {
    ...
  }
};

class Bar
{
public:
   const IProcess& getProcess() const {return ...;}
   IProcess& getProcess() {return ...;}

   void doOtherWork {
    getProcess().doSomeWork();        
  }
};

Llamando

getProcess().doSomeWork();

siempre dará como resultado una llamada a

IProcess& getProcess()

¿Hay otra manera de llamar

const IProcess& getProcess() const 

de una función miembro no constante? Hasta ahora he utilizado

const_cast<const Bar*>(this)->getProcess().doSomeWork();

que hace el truco pero parece demasiado complicado.


Editar: Debo mencionar que el código se está refaccionando y, finalmente, solo quedará una función.

<*>

Sin embargo, actualmente hay un efecto secundario y la llamada const puede devolver una instancia diferente de IProcess en algún momento.

Por favor, sigue el tema.

¿Fue útil?

Solución

Evitar el lanzamiento: asigne esto a un const Bar * o lo que sea y use eso para llamar a getProcess () .

Hay algunas razones pedantes para hacer eso, pero también hace que sea más obvio lo que estás haciendo sin forzar al compilador a hacer algo potencialmente inseguro. Por supuesto, es posible que nunca llegue a esos casos, pero también podría escribir algo que no use un elenco en este caso.

Otros consejos

const_cast es para lanzar lejos ¡constness!

Está cambiando de non-const a const, lo que es seguro, así que use static_cast :

   static_cast<const Bar*>(this)->getProcess().doSomeWork();

Me refiero a que, técnicamente, se puede convertir en constancia con const_cast , pero no es un uso pragmático del operador. El propósito de los nuevos lanzamientos de estilo (en comparación con el antiguo modelo de estilo C), es comunicar la intención del elenco. const_cast es un olor a código, y su uso debe revisarse al menos. static_cast por otro lado es seguro. Pero es una cuestión de estilo C ++.

O puedes crear un nuevo método const (privado), y llamarlo desde doOtherWork :

  void doSomeWorkOnProcess() const { getProcess().doSomeWork(); }

Usar una const temporal también es una opción (respuesta de " MSN "):

   const Bar* _this = this;
   _this->getProcess().doSomeWork();

Si getProcess () y getProcess () const no devuelven una referencia al mismo objeto (pero están calificados de manera diferente), esto indicaría un diseño deficiente de Barra de clase . La sobrecarga en la const ness de la función no es una buena manera de distinguir las funciones con diferentes comportamientos.

Si están devolviendo una referencia al mismo objeto, entonces:

const_cast<const Bar*>(this)->getProcess().doSomeWork();

y

getProcess().doSomeWork();

llame exactamente a la misma función doSomeWork () , por lo que no es necesario usar const_cast .

Si la conversión es demasiado fea para usted, en su lugar, podría agregar un método a Bar que simplemente devuelva una referencia constante a * this :

Bar const& as_const() const {
    return *this;    // Compiler adds "const" without needing static_cast<>
}

A continuación, puede llamar al método a cualquier const en la Barra simplemente al añadir as_const (). , por ejemplo:

as_const().getProcess().doSomeWork();

No tiene que hacer ningún truco de lanzamiento si la función no está sobrecargada. Llamar a un método const de un objeto no const es correcto. Está llamando a un método no constante desde un objeto const que está prohibido. Si los métodos se anulan con una función const y non-const, entonces convertir el objeto en const hará el truco:

const_cast<const IProcess&> (getProcess()).doSomeWork();

EDITAR: No leí toda la pregunta. Sí, necesita const_cast el este puntero o hacer que la función doOtherWork se convierta en const IProcess & amp; getProcess () const .

El punto es que no necesita un objeto const para llamar a doSomeWork . Dado que ese es el objetivo, ¿necesita el método const llamado?

Otra opción sería cambiar el nombre de las funciones sobrecargadas. Esta sería una muy buena idea si las dos funciones realmente tuvieran diferentes comportamientos / efectos secundarios. De lo contrario, el efecto de la llamada a la función no sería obvio.

define una plantilla

template< class T >
const T & addConst ( T & t ) 
{
    return t;
}

y llame

addConst( getProcess() ).doSomeWork();

Bueno, ¿puedes declarar

void doOtherWork const ()

?

Eso lo haría.

Creo que el método const_cast es tu mejor opción. Esto es solo una limitación del marco const en C ++. Creo que la única forma de evitar la conversión es definir un método que devuelva la instancia const IProcess independientemente. Por ejemplo.

const IProcess* getProcessConst() const { return ... }
...
getProcessConst().doSomeWork();

Supongo que quieres que DoOtherWork llame a una de tus dos llamadas getprocess dependiendo de si se llama desde un objeto const o no.

Lo mejor que puedo sugerir es esto:

class Bar
{
public:
   const IProcess& getProcess() const {return ...;}
   IProcess& getProcess() {return ...;}

   void doOtherWork {            // should use getProcess()      
    getProcess().doSomeWork();        
  }
   void doOtherWork const {
    getProcess().doSomeWork();   // should use getProcess() const     
  }
};

Incluso si eso funciona, esto me parece un mal olor. Me preocuparía mucho que el comportamiento de la clase cambie radicalmente de acuerdo con la constancia de un objeto.

  

Publicado por monjardin
   Otra opción sería cambiar el nombre de las funciones sobrecargadas. Esta sería una muy buena idea si las dos funciones realmente tuvieran diferentes comportamientos / efectos secundarios. De lo contrario, el efecto de la llamada a la función no sería obvio.

IProcess & amp; se accede en otro código principalmente a través de una propiedad

__declspec(property(get=getProcess)) IProcess& Process;

así que cambiar el nombre no era una opción. La mayoría de las veces const de la función de llamada coincide con getProcess (), por lo que no hubo ningún problema.

Básicamente, te quedas con el cambio de nombre del otro método o const_cast.

Por cierto, esta es una de las razones por las que los punteros inteligentes de copia en escritura no funcionan bien en C ++. Un puntero inteligente de copia en escritura es uno que puede compartirse infinitamente. Cuando un usuario accede a los datos en un contexto no constante, se hace una copia de los datos (si el usuario no tiene la referencia única). Este tipo de puntero puede ser muy conveniente de usar cuando se comparten grandes estructuras de datos que solo algunos clientes necesitan modificar. El más " lógico " la implementación es tener un operador const y un operador no const- > ;. La versión const simplemente devuelve la referencia subyacente. La versión no constante realiza la comprobación de referencia única y la copia. No es útil porque un puntero inteligente no constante utilizará el operador no constante > por defecto, incluso si quisieras usar la versión const. El requisito de const_cast lo hace muy hostil para el usuario.

Doy la bienvenida a cualquiera que demuestre que estoy equivocado y muestra un puntero de copia en escritura fácil de usar en C ++ ...

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top