2014-10-24 10 views
8

Supponiamo che ho una classe Foo che ha un puntatore privato ad una Bar:È accettabile eliminare la costanza in un costruttore di mosse?

class Foo 
{ 
private: 
    Bar * bar; 

public: 
    Foo() : bar (new Bar()) 
    {} 

    ~Foo() 
    { 
    delete bar; 
    } 
}; 

Se il puntatore bar dovrebbe mai essere riassegnato a una diversa istanza, allora ha senso per rendere il puntatore stesso const al fine di fermare me (o un manutentore) dal farlo in futuro:

private: 
    Bar * const bar; 

mi piace fare questo ovunque l'occasione.

Se dunque io volevo scrivere un costruttore mossa, sarebbe simile a questa:

Foo (Foo && f) : 
    bar (f.bar) 
{ 
    f.bar = NULL; // uh oh; f.bar is const. 
} 

posso "fare l'errore di andare via" o gettando via la costanza di f.bar o non farcela const in primo luogo. Nessuno dei quali sono cose che voglio fare. Preferirei non rimuovere completamente il const perché è lì per un motivo. D'altra parte, gettare la costanza suona campanelli d'allarme per me ed è qualcosa che non faccio mai. La mia domanda è: è considerata una pratica accettabile per gettare via la costanza all'interno di un costruttore di mosse come questo? C'è un modo migliore che non ho considerato?

non credo che la mia domanda è la stessa come questo: Use const_cast to implement the move constructor

+7

Se è necessario modificare 'bar', in modo abbastanza chiaro non dovrebbe essere' const'. Pensa attentamente al tuo design. – user657267

+0

Sembra che la tua classe non debba essere spostata. – juanchopanza

+2

L'utilizzo di const_cast qui è un comportamento non definito. –

risposta

6

Se in alcuni casi è necessario modificare il puntatore (anche se si tratta di un solo caso), probabilmente non dovrebbe essere const.

Tuttavia, anche mettendo da parte questo pensiero, utilizzando const_cast per rimuovere la costanza da un oggetto e quindi utilizzando il risultato del cast viene richiamato un comportamento non definito. È sicuro di const_cast una variabile che originariamente non era const.

+1

Più precisamente, è un comportamento indefinito scrivere sul risultato del cast. Puoi ancora leggere dal risultato. –

+0

Immagino che questa lezione sia L'UNICA COSA che ho imparato quest'anno sull'uso corretto e non definito di provocazione comportamentale di const_cast: "Non modificare mai una variabile la cui dichiarazione è const". – paercebal

4

Tu sei fondamentalmente da soli contraddicendo.
Stai dicendo "Non voglio cambiarlo in nessuna circostanza", e stai dicendo "Voglio cambiarlo quando sposto un oggetto".

Quindi, se c'è uno scenario in cui deve essere modificato, non è un const e puoi stare tranquillo rimuovendolo.

Se si è determinato che lo si desidera const, è possibile aggiungere un altro valore booleano che determina la proprietà (se è necessario gestire la vita delle risorse). Quindi l'oggetto appuntito può essere rilasciato solo quando viene chiamato un distruttore che possiede l'oggetto.

+0

Sono d'accordo che mi contraddico. Non voglio che cambi in nessuna circostanza, con la sola eccezione del costruttore di mosse. Suppongo che quello che sto dicendo è che è un peccato che JUST il costruttore di move mi impedisca di usare const. – peterpi

+2

Mostra che il tuo design ha un difetto. C'è un caso in cui quel puntatore può essere cambiato. Esempio: '{Foo f; Fo f2 (std :: move (f)); f.somefn(); } ' Niente di intrinsecamente sbagliato con quel frammento di codice, ma somefn() avrebbe dovuto poter contare sul valore della barra invariata. Avresti detto chiaramente al compilatore (e ai futuri lettori del tuo codice) che una volta che la barra è stata inizializzata, non cambierà mai. Ma poiché l'oggetto è stato spostato, da esso è cambiato. Così violando la costanza del membro. –

+0

@AndreKostur Questa è una grande spiegazione, grazie. Mostra che "l'aggiunta della mobilità" è più che l'aggiunta di un costruttore di mosse. In tutte le funzioni membro deve essere considerato il comportamento di un oggetto * post move *. – peterpi

3

Se possibile, utilizzare unique_ptr invece:

std::unique_ptr<Bar> bar; 
Foo(Foo&& f) : bar(std::move(f.bar)){} 
// or maybe you won't even have to declare move constructor 

In questo modo non solo si otterrà maggiore sicurezza e comfort, ma anche non si accidentalmente riscrivere con =, dovrete chiamare .reset() quindi ci penserai due volte prima di farlo (che è il tuo scopo, suppongo).

Problemi correlati