Domanda

Devo chiamare una funzione const da un oggetto non const. Vedi esempio

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();        
  }
};

Chiamata

getProcess().doSomeWork();

si tradurrà sempre in una chiamata a

IProcess& getProcess()

C'è un altro modo di chiamare

const IProcess& getProcess() const 

da una funzione membro non costante? Finora ho usato

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

che fa il trucco ma sembra eccessivamente complicato.


Modifica: dovrei menzionare che il codice è in fase di refactoring e alla fine rimarrà solo una funzione.

<*>

Tuttavia, attualmente c'è un effetto collaterale e la chiamata const può restituire una diversa istanza di IProcess qualche volta.

Continua sull'argomento.

È stato utile?

Soluzione

Evita il cast: assegnalo a una const Bar * o qualsiasi altra cosa e usala per chiamare getProcess () .

Ci sono alcuni motivi pedanti per farlo, ma rende anche più ovvio ciò che stai facendo senza forzare il compilatore a fare qualcosa di potenzialmente pericoloso. Certo, potresti non toccare mai quei casi, ma potresti anche scrivere qualcosa che non usa un cast in questo caso.

Altri suggerimenti

const_cast è per lanciare via costanza!

Stai trasmettendo da non const a const che è sicuro, quindi usa static_cast :

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

Intendo tecnicamente parlando puoi lanciare in coerenza con const_cast , ma non è un uso pragmatico dell'operatore. Lo scopo dei nuovi cast di stile (rispetto al vecchio cast in stile c) è comunicare l'intento del cast. const_cast è un odore di codice e il suo utilizzo dovrebbe essere rivisto almeno. static_cast invece è sicuro. Ma è una questione di stile C ++.

Oppure puoi creare un nuovo metodo const (privato) e chiamarlo da doOtherWork :

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

L'uso di una const temporanea è anche un'opzione (risposta di " MSN "):

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

Se getProcess () e getProcess () const non stanno restituendo un riferimento allo stesso oggetto (ma differentemente qualificato), allora indicherebbe una cattiva progettazione di barra di classe . Il sovraccarico sulla const della funzione non è un buon modo per distinguere le funzioni con comportamenti diversi.

Se stanno restituendo un riferimento allo stesso oggetto, allora:

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

e

getProcess().doSomeWork();

chiama esattamente la stessa funzione doSomeWork () , quindi non è necessario utilizzare const_cast .

Se il cast è troppo brutto per te, puoi invece aggiungere un metodo a Bar che restituisce semplicemente un riferimento const a * this :

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

Puoi quindi chiamare qualsiasi const in Bar semplicemente anteponendo as_const (). , ad es .:

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

Non è necessario eseguire alcun trucco di lancio se la funzione non è sovraccarica. Chiamare un metodo const di un oggetto non const va bene. Sta chiamando un metodo non const da un oggetto const proibito. Se i metodi vengono sovrascritti con una funzione const e non const, allora lanciare l'oggetto su const farà il trucco:

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

EDIT: non ho letto l'intera domanda. Sì, devi const_cast il this puntatore o rendere la funzione doOtherWork const per chiamare const IProcess & amp; getProcess () const .

Resta il fatto che non è necessario un oggetto const per chiamare doSomeWork . Dato che questo è l'obiettivo, hai bisogno del metodo const chiamato?

Un'altra opzione sarebbe quella di rinominare le funzioni sovraccaricate. Sarebbe davvero una buona idea se le due funzioni avessero effettivamente comportamenti / effetti collaterali diversi. Altrimenti, l'effetto della chiamata di funzione non sarebbe ovvio.

definisce un modello

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

e chiama

addConst( getProcess() ).doSomeWork();

Bene, puoi dichiarare

void doOtherWork const ()

Quello lo farebbe.

Penso che il metodo const_cast sia la tua migliore opzione. Questa è solo una limitazione del framework const in C ++. Penso che l'unico modo per evitare il casting sia definire un metodo che restituisca un'istanza const IProcess a prescindere. Per esempio.

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

Presumo che tu voglia che DoOtherWork chiami una delle tue due chiamate getprocess a seconda che sia chiamata da un oggetto const o meno.

Il meglio che posso suggerire è questo:

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

Anche se funziona, mi sembra un cattivo odore. Sarei molto diffidente nei confronti del comportamento di classe che cambia radicalmente in base alla costanza di un oggetto.

  

Inserito da monjardin
   Un'altra opzione sarebbe quella di rinominare le funzioni sovraccaricate. Sarebbe davvero una buona idea se le due funzioni avessero effettivamente comportamenti / effetti collaterali diversi. Altrimenti, l'effetto della chiamata di funzione non sarebbe ovvio.

iProcess & amp; si accede in altro codice principalmente attraverso una proprietà

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

quindi la ridenominazione non era un'opzione. La maggior parte del tempo const della funzione chiamante corrisponde a getProcess (), quindi non si sono verificati problemi.

Sei praticamente bloccato con la ridenominazione dell'altro metodo o const_cast.

A proposito, questo è uno dei motivi per cui i puntatori intelligenti copy-on-write in realtà non funzionano bene in C ++. Un puntatore intelligente copia su scrittura è uno che può essere condiviso all'infinito. Quando un utente accede ai dati in un contesto non const, viene creata una copia dei dati (se l'utente non detiene il riferimento univoco). Questo tipo di puntatore può essere molto comodo da usare quando si condividono strutture di dati di grandi dimensioni che solo alcuni client devono modificare. Il più "logico" l'implementazione deve avere un operatore const e un non const- > ;. La versione const restituisce semplicemente il riferimento sottostante. La versione non const esegue il controllo e la copia univoci dei riferimenti. Non è utile perché un puntatore intelligente non const utilizzerà l'operatore non const- > per impostazione predefinita, anche se si desidera utilizzare la versione const. Il requisito const_cast lo rende molto ostile agli utenti.

Sto dando il benvenuto a chiunque mi dimostri di avere torto e mostra un puntatore copy-on-write intuitivo in C ++ ...

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top