Newsgroup: it.comp.lang.c++
Titolo: Chiarimento sul tipo di ritorno di funzione

Cosa significa la & dopo il tipo di ritorno di una funzione?
Es: complex& add(complex& c);
Grazie anticipatamente.
Gamello.
-- 
Creato con il rivoluzionario client e-mail di Opera:  
http://www.opera.com/mail/

 Es: complex& add(complex& c);
Che si sta restituendo un riferimento ad un complex, non un nuovo 
complex. Nel caso specifico immagino che il valore di ritorno sia "c" 
stesso.
E.

 Es: complex& add(complex& c);
Quando vedi qualcosa che ritorna un reference è bene drizzare le
orecchie.
Nota bene, non perchè la cosa sia in se sbagliata. Come ha scritto manu
sarebbe perfettamente lecito ritornare un reference a c, o anche un
reference all'oggetto stesso (nel caso in cui add sia una funzione
membro, per dire).
Solo se ritorni un reference a una variabile automatica locale stai
commettendo un errore. Sfortunatamente è un errore che capita fin troppo
spesso.
-- 
blog:  
http://www.akropolix.net/rik0/blogs
 | Uccidete i filosofi,
site:  
http://www.akropolix.net/rik0/
      | tenetevi riso e
forum: 
http://www.akropolix.net/forum/
     | bacchette per voi.

 spesso.
Le uniche cose che considero accettabile ritornare per reference sono
un parametro ricevuto altrettanto per reference oppure *this, ovvero i
due casi che menzioni esplicitamente.
Esistono tanti altri casi che, se anche non costituiscono errore nel
senso stretto del termine, vanno comunque evitati. Questo perche'
restituire per reference equivale a impegnarsi a garantire l'esistenza
dell'oggetto ritornato fino al termine dell'esecuzione del programma e
raramente chi programma e' consapevole di questo.
Anche restituire oggetti allocati staticamente puo' essere pericoloso
se non sono scalari, perche' si puo' incappare in problemi di
ordinamento dei distruttori. Mi e' capitato di avere un'applicazione
che dava un'Access Violation in uscita. Alla fine si trattava di un
singleton che faceva riferimento ad una std::string statica che veniva
distrutta prima di lui.
Ciao,
Nicola

 ordinamento dei distruttori. Mi e' capitato di avere un'applicazione
Confermo, mi e` capitato nel portare un'applicazione da VC++ a gcc di 
avere problemi (in un'applicazione single thread) con member function 
che eseguivano manipolazioni su stringhe e ritornavano un reference ad 
un oggetto statico interno, piu' o meno facevano questo:
std::string dest = elab(src1) + elab(src2);
su VC++ avevo dest corretta, mentre su GCC era come se facessi:
dest = elab(src1) + elab(src1);
Bye,
  Gabry

 distrutta prima di lui.
Io questo con attenzione ogni tanto lo faccio. Chiaramente mi procuro
che la loro distruzione non sia problematica (esempio perchè l'unica
risorsa che prendono è memoria) e non ci siano interdipendenze. Sono
comunque d'accordo sull'andarci con i piedi di piombo.


 se non sono scalari, 
per scalari intendi tipi intrinseci privi, diciamo così, di 
costruttori user defined ?
Lo chiedo perché l'esempio che hai citato mi ha messo inquietudine.
In effetti per non poche funzioni ho usato la notazione di 
ritornare reference, i quali facevano riferimento a buffers 
statici referenziati al loro interno (quindi locali alla 
funzione) o al container della funzione (la classe).
Per pura coincidenza ho sempre usato buffers di tipi intrinseci, 
tipo char, int, double et simili.
Non mi sono mai accorto di potenziali problemi, ma forse è stata 
una coincidenza.
Potresti confermare che, almeno per i tipi intrinseci (se sono 
quelli che chiami scalari) questa pratica non nasconda insidie 
difficili da individuare.
P.S. so che persino alcune vecchie funzioni di libreria C (tipo 
ultoa, e altre di conversione buffer based, ritornavano 
puntatori a roba statica interna, che se non sono reference, e 
quindi sono meglio testabili contro null, pure di fatto 
potrebbero avrere il medesimo problema della potenziale 
distruzione anzitempo del buffer.
A meno che questo sia pertinente per i soli tipi dotati di 
costruttori, o cmq non scalari
ciao
Soviet_Mario


 
...
IMHO anche restituire un reference a un membro è perfettamente lecito 
(ma credo che quando dicevi *this, in qualche modo prendevi in 
considerazione anche i menbri)
Anzi, spesso è proprio consigliabile che le funzioni "getter" tornino 
dei reference const quando si tratta di membri non piccoli.
GM

 dei reference const quando si tratta di membri non piccoli.
Anche se non sono piccoli. Ad esempio vector::operator[].
E.

 considerazione anche i menbri)
Lecito e *pericolosissimo*. In primo luogo è una violazione piuttosto
evidente dell'encapsulation (in determinati casi). Inoltre tali handles
possono essere usati per scavalcare la constness logica di un oggetto,
per non parlare del problema dei dangling handles (ovvero tale
riferimento se non stai attento ti viene invalidato quando meno te lo
aspetti).
Insomma, siamo sempre nel campo delle cose che 'si possono fare', ma
bisogna starci dannatamente attenti. Suggerisco "Avoid returning handles
to object internals" di Meyers.


 due casi che menzioni esplicitamente.
Consideri accettabili i seguenti due metodi?
template<typename T, int N>
class C {
public:
	T& operator[](int i) { return impl_[i]; }
	const T& operator[](int i) const { return impl_[i]; }
private:
	T impl_[N];
};
-- 
     Carlo Milanesi
 
http://digilander.libero.it/carlmila

 aspetti).
Qui invece non  ti seguo: com'č possibile che diventi non valido il ref 
ad un membro?
GM

 ad un membro?
Nel modo ovvio: quando muore l'oggetto. Se non si fa attenzione (è
chiaro che siamo nel mondo degli *errori* a questo punto) uno si fa dare
un riferimento ad un membro e questo riferimento sopravvive all'oggetto
stesso.
Poi per stare meno sul banale non vedo nulla di strano in un oggetto che
in seguito a date operazioni invalida buona parte dei riferimenti
interni. Pensa per dire ai vector e agli iteratori: è noto che
un'aggiunta in presenza di iteratori deve venire verificata perchè può
invalidare gli iteratori.
Cose interessanti possono accadere con i pimpl, per dire. Ho un
riferimento ad un membro di a, swappo a e b, a è distrutto e il mio
riferimento resta pendente. Sono tutti errori, chiaramente.
Inoltre un oggetto è libero di manipolare dentro di se gli oggetti come
crede, e non necessariamente questo è fatto in modo da non distruggere i
riferimenti.
In generale quando ritorno un riferimento dentro un oggetto sto facendo
una cosa piuttosto forte a livello di dipendenze e di codice che se ne
va in giro. 
Insomma, io tendo a ritornare copie e quando è dimostrato che questo è
causa di inefficienza vedo se usare il riferiemento è troppo costoso.
Ah, ecco. Ti faccio un esempio.
class Employee {
   string name_;
public:
   const string& name();
}
const Employee getBoss(const Dipartment& dip);
...
Dipartment my_dip = Dipartment::current_dipartment();
const string* p_name = &(getBoss(my_dip).name());
Questo codice, oltre che chiaramente poco astuto (ma credo funzioni
anche mettendo p_name come reference e togliendo il &) è *sbagliato*.
Sempre e comunque.


 
pare di no.
Considereresti accettabili versioni piů ligie del tipo seguente ?
template<typename T, int N>
class C {
public:
     T& operator[](int i)
	{
	if ((i >=0 ) && (i < N)) return impl_[i];
	throw ("OUT OF RANGE");
	}
     const T& operator[](int i) const
	{
	if ((i >=0 ) && (i < N)) return impl_[i];
	throw ("OUT OF RANGE");
	}
private:
     T impl_[N];
};
Nel caso non lo siano, perchč non lo sono ?
Cioč, come si č scritto recentemente, un'idea l'avrei, anche se 
di quelle malsane, tipo
int & Funz ()
{
C <char, 10> Arr;
return (Arr [5]);
}
int main (void)
{
int & HandleInt = Funz ();
HandleInt = 174; // a cosa punta HandleInt ora ?
return;
};
perň un simile esempio č debole non tanto per il fatto di 
ritornare reference ad "internals", quanto per il fatto di 
appoggiarsi su una variabile a lifetime fuori del controllo del 
chiamante o non cmq statica.
se Arr fosse stato dichiarato static dentro Funz, non si sarebbe 
presentato il comportamento indefinito, no ?
ciao
Soviet_Mario