Newsgroup: it.comp.lang.c++
Titolo: Mutable and const member function

Salve,
addentrandomi nella lettura dello Stroustrup sono giunto all'argomento
citato nel subj.
Ordunque, se non ho capito male quando nella definizione di una classe
si dichiarano _const_ delle member func. si intende esplicitare il
fatto che esse *non* devono variare lo stato dei member data. 
esempio:
class X {
	public:
	....
	void Dosomething(...) const;
	....
}
void X::Dosometing(....) const
{
	....
}
Se nel body della Dosomething ci metto qualche istruzione che modifica
variabili appartenenti alla classe X... ciomp! Il comnpilatore si
arrabbia. OK?
A questo punto entra in gioco la clausola _mutable_, che, come dice il
nome stesso, dichiara una variabile appunto cangiante :-) .
Da quanto ho capito leggendo se una member fun. dichiarata const varia
lo stato di una var mutable il compilatore sta zitto e fa il suo
dovere, ci ho beccato?
Esempio:
class X {
	private:
	   mutable sometype varia;
	....
	public:
	....
	void VeryDummy(sometype& viariera) const;
	....
}
void X::VeryDummy(sometype variera) const
{
	....
	variera++;
	...
}
ammesso che variera++ abbia un qualche senso, l'invocazione della mem
fun VeryDummmy *dovrebbe* essere lecita. Io ho capito, cosi'...
Quindi mi sono chiesto quale possa essere l'utilita' di usare mutable
congiuntamente alle memfun const, dato che basterebbe _segare_ il
const dalla memfun rendendo cosi' superfluo il mutable.
Cmq, dato che in c++ *non* esiste niente di superfluo (semmai si va al
risparmio di keyword), qualche ragione ci sara`... a me viene da
pensare che quando si ha a che fare con classi belle ciccione e
incasinate, magari collegate in gerarchia, l'uso della mutable e delle
memfun const possa, per cosě dire, parare il didietro da eventuali
pasticci derivanti da errori di programmazione o peggio da errori di
design?
Ci ho beccato?
Magari se qualche buona anima potesse illustrarmi i casi di utilizzo
piu` tipici...
Graie.
Droide.

Ci ho beccato?
Nah, nulla di cosi` complicato.  Una member function viene dichiarata
const quando lo e` "logicamente", e un membro mutable e` uno che
si puo` cambiare senza che cambi "logicamente" l'oggetto a cui
appartiene; normalmente per scopi di ottimizzazione, ovvero di un
qualche monitoraggio (conteggio degli accessi).
Vediamo un esempio di ottimizzazione.  Supponi di avere una lista
ma volere *fingere* che sia 'quasi' un array per qualche ragione -- se
gli accessi sono "normalmente" sequenziali o quasi, ci puoi riuscire
con una piccola ottimizzazione:
template <class T>
class array: private list<T> {
    list<T>::iterator latest_iterator;
    int latest_index;
public:
    array(): latest_iterator(begin()), latest_index(0) {}
    using list<T>::push_back;
    T& operator[](int i) {
        if(i<latest_index) {
            while(i<latest_index) {
                --latest_index; --latest_iterator;
            }
        } else {
            while(i>latest_index) {
                ++latest_index; ++latest_iterator;
            }
        }
        return *latest_iterator;
    }
};
Carino e potenzialmente utile -- se non ci si trova mai
a fronte di array<T> che sono costanti... per questi
ultimi vorremmo fare lo stesso trucco (con operator[]
const, e facendogli tornare un T oppure const T&),
ma come si fa, visto che, puramente per ragioni di
prestazioni, dobbiamo variare latest_iterator e latest_index?
Risposta: rendi mutable questi due membri, e vai felice...!
Alex

 qualche monitoraggio (conteggio degli accessi).
Grazie per l'esempio, pero' a me la sensazione che il Droide
Allucinato avesse ragione dicendo che e' un metodo "per pararsi il
culo" resta.....
In ogni caso le cose sporche mi piacciono, quindi vedro' di farne
abbondante uso nei miei futuri (e mal designed) programmi...
Personalmente mi ricordavo che la keyword mutable si usasse anche per
indicare delle variabili dove si fanno delle cose talmente allucinanti
(che so, accesso attraverso una indirezione quadrupla) che e'
necessario che l'ottimizzatore non provi neanche a mettere le mani,
perche' tanto sicuramente farebbe casino.
Ciao,
Alberto
-- 
Alberto BARSELLA
PGP fingerprint = 13 3F 22 D2 0B 0A D3 25  F1 89 FE B5 82 AD 75 2A
** Beliefs are dangerous. Beliefs allow the mind to stop functioning.
A non-functioning mind is clinically dead.  Believe in nothing... **

};
Hummm... questo e` un pelino troppo sofisticato (template e iteratori
ancora non li ho studiati), me lo devo studiare con attenzione,
prometto. Colgo l'occasione pero` di fare una domanda sulla sintassi
dei costruttori. Nel tuo esempio compare:
array(): latest_iterator(begin()), latest_index(0) {}
        ^^^^
Ok, dopo i *:* cosa compare? Il Soustr.(relativamente al capitolo in
cui sono arrivato)  ha solo brevemente accennato a una tale sintassi.
Io non ho capito bene. A livello intuitivo direi che dopo i ':' si
possono (o devono?) mettere i costruttori delle classi membro della
classe per la quale si sta definendo il costruttore, classi membro per
le quali si vuole istanziare non al valore di default. Non so se mi
sono espresso chiaramente.
Ciao
 Droide.

perche' tanto sicuramente farebbe casino.
Quello, se mai, e` "volatile".
Alex

sono espresso chiaramente.
Chiarissimamente, e correttamente: per essere piu` precisi e
completi, quello che si mette e` un elenco di _classi base_, e
variabili d'istanza, da inizializzare diversamente che col default
constructor (inoltre, per variabili d'istanza che sono reference,
e` obbligatorio inizializzarle in questa forma).
Attenzione: l'ordine di valutazione di questi inizializzatori non e`
quello scritto, ma quello in cui le basi, e poi le variabili
d'instanza, si presentano nella dichiarazione delle classe; i
buoni compilatori (ahime non VC++ 5...!) avvertono quando
c'e` una discrepanza fra l'ordine delle dichiarazioni e quello
in cui vengono scritte le inizializzazioni, ma in mancanza di un
tale warning e` bene abituarsi a scrivere comunque le cose
nell'ordine giusto...
Alex