Presumo che il tuo obiettivo sia la vera immutabilità - ogni oggetto, una volta costruito, non può essere modificato. Non è possibile assegnare un oggetto a un altro.
Il più grande svantaggio del tuo progetto è che non è compatibile con la semantica del movimento, il che può rendere le funzioni che rendono tali oggetti più pratici.
Per fare un esempio:
struct RockSolidLayers {
const std::vector<RockSolid> layers;
};
possiamo creare uno di questi, ma se abbiamo una funzione per crearlo:
RockSolidLayers make_layers();
si deve (logicamente) copiare il suo contenuto verso il restituire il valore o utilizzare la sintassi return {}
per costruirlo direttamente. All'esterno, devi fare:
RockSolidLayers&& layers = make_layers();
o ancora (logicamente) copia-costruisci. L'incapacità di spostare-costruire ostacolerà una serie di semplici modi per avere un codice ottimale.
Ora, sia di quelle di copia-costruzioni sono eliso, ma il caso più generale in possesso - non si può spostare i dati da un oggetto chiamato ad un altro, come C++ non ha un'operazione di "distruggere e spostare" che entrambi prendono una variabile fuori dall'ambito e la usano per costruire qualcos'altro.
E i casi in cui C++ sposta implicitamente l'oggetto (return local_variable;
per esempio) prima della distruzione vengono bloccati dai membri di dati const
.
In un linguaggio progettato intorno a dati immutabili, sarebbe in grado di "spostare" i dati nonostante la sua (logica) immutabilità.
Un modo per risolvere questo problema consiste nell'utilizzare l'heap e archiviare i dati in std::shared_ptr<const Foo>
. Ora il numero const
non è nei dati dei membri, ma piuttosto nella variabile. Puoi anche esporre solo le funzioni di fabbrica per ognuno dei tuoi tipi che restituisce il precedente shared_ptr<const Foo>
, bloccando altre costruzioni.
Tali oggetti possono essere composti, con Bar
memorizzando membri std::shared_ptr<const Foo>
.
Una funzione che restituisce un std::shared_ptr<const X>
può spostare in modo efficiente i dati e una variabile locale può avere il suo stato spostato in un'altra funzione una volta che hai finito senza poter fare confusione con i dati "reali".
Per una tecnica correlata, è idomatic in C++ meno vincolato a prendere tale shared_ptr<const X>
e memorizzarli in un tipo di wrapper che finge di non essere immutabile. Quando si esegue un'operaiton mutante, lo shared_ptr<const X>
viene clonato e modificato, quindi memorizzato. Un'ottimizzazione "sa" che lo shared_ptr<const X>
è "veramente" un (nota: assicurarsi che le funzioni di fabbrica restituiscano un cast a shared_ptr<const X>
o che questo non sia effettivamente vero), e quando lo use_count()
è 1 invece getta via const
e lo modifica direttamente . Questa è un'implementazione della tecnica nota come "copia su scrittura".
Dipende molto dal tuo concetto desiderato di immutabile. Prendi in considerazione le stringhe Java e C#. Sono immutabili ma assegnabili. –
I membri di dati 'const' hanno un vantaggio in quanto si ottiene un errore se si dimentica di inizializzare tale membro di tipo base. Uno svantaggio è che non è possibile assegnare a variabili del tipo. –
Uno svantaggio principale è che non è possibile spostare i dati da un oggetto se i suoi membri sono 'const', motivo per cui preferirei rendere privati i membri e fornire invece getter e metodi' const' invece come suggerisce oikosdev – Joe