Newsgroup: it.comp.lang.c++
Titolo: Overloading degli operatori e ordine degli operandi

Cambiando l'ordine degli operandi... il C++ fa cilecca?
Supponiamo di avere a disposizione una classe chiamata MiaClasse. Supponiamo
inoltre di avere fatto l'overloading dell'operatore "+" attraverso due
funzioni:
MiaClasse MiaClasse::operator+(MiaClasse operando_dx);
MiaClasse MiaClasse::operator+(int operando_dx);
Se scrivo:
MiaClasse a, b, r;
a.Set(21,5);
b.Set(12,3);
r = a + b; // operando sx di tipo MiaClasse, operando dx di tipo MiaClasse
funziona regolarmente. Se poi scrivo;
MiaClasse a, r;
a.Set(21,5);
c = a + 2; // operando sx di tipo MiaClasse, operando dx di tipo int
funziona regolarmente anche questo. Ma se scrivo:
MiaClasse a, r;
a.Set(21,5);
c = 2 + a; // operando sx di tipo int, operando dx di tipo MiaClasse
non funziona affatto. La mia domanda è questa: è possibile che un linguaggio
così potente, stimato e osannato come il C++ non abbia una soluzione a
questo problema che non è solo puramente "semantico", ma anche di comodità
per il programmatore?
Kaos.

 Kaos.
Francesco

 c = 2 + a; // operando sx di tipo int, operando dx di tipo MiaClasse
Per rendere l'operatore simmetrico non devi definirlo come membro di
classe.
EMG
--
EMAG Software Homepage:     
http://www.emagsoftware.it
The EMG's ZX-Spectrum Page: 
http://www.emagsoftware.it/spectrum
The Best of Spectrum Games: 
http://www.emagsoftware.it/tbosg

 "semantico", ma anche di comodità per il programmatore?
La soluzione c'e', ma non s'implementa nel modo che hai descritto. Il
comportamento nell'ultimo caso e' dovuto al fatto che non esiste un
operatore + del tipo predefinito int che accetti come parametro un
MiaClasse. Infatti l'espressione potrebbe essere anche scritta:
c.operator=(2.operator+(a));
Se pero' MiaClasse e' convertibile implicitamente in int con un
costruttore non explicit del tipo MiaClasse(int n) allora puoi scrivere
un'operatore + globale (friend se devi accedere a membri non pubblici di
MiaClasse) che prenda in input due parametri di tipo MiaClasse. Es.
class MiaClasse {
public:
   MiaClasse() : m(0) {};
   MiaClasse(int v) : m(v) {}; //conversione implicita
   friend MiaClasse operator+(const MiaClasse& lhs, const MiaClasse&
rhs);
//ecc
private:
   int m;
};
MiaClasse operator+(const MiaClasse& lhs, const MiaClasse& rhs)
{
   return MiaClasse(lhs.m + rhs.m);
}
La tua espressione ora diventa:
c.operator=(operator+(2, a));
con 2 che viene convertito implicitamente in MiaClasse grazie al
costruttore.
Ciao
Gian Michele

 per il programmatore?
la soluzione esiste ed è anche molto semplice da attuare
data la classe MiaClasse definita così:
class MiaClasse{
    public:
        ....
        const MiaClasse& operator+( int a );
        ...
};
aggiungi una funzione friend di questo tipo alla sua definizione:
    friend const MiaClasse operator+(int a, const MiaClasse& obj);
friend ti serve se devi accedere ai dati interni di MiaClasse (praticamente
x il 99% delle volte :-) )
e avrai ottenuto la simmetricità dell'operatore + (MiaCLasse - int) che
volevi.

proteggerli da modifiche indesiderate puoi metterli const)
Un reference e' associato ad un oggetto. Questo oggetto da
chi sarebbe creato ? quanto vivrebbe ? da chi sarebbe distrutto ?
In generale un consiglio dato e' proprio l'opposto: se
implementi operator+ *NON* farti tentare dall'idea di
restituire un reference a meno che tu sia obbligato e
anche pronto ad affrontare un scacco di grane conseguenti.
Il discorso e' diverso per gli operatori di modifica come
operator+=, dove restituire *this come reference e' la
prassi di gran lunga piu' comune.
HTH
Andrea

 anche pronto ad affrontare un scacco di grane conseguenti.
Tanto più che il compilatore potrebbe anche ottimizzare via la necessità
della copia del valore di ritorno.


 ha scritto nel messaggio
news:n2rr7v03m38cokb31...@4ax.com...


hai ragione (ma dove avevo la testa?)
mi sono espesso male in questo caso.
nel caso di
class MiaClasse{
    public:
        ....
        const MiaClasse& operator+( int a );
        ...
};
è vero, ho sbagliato io...
 ma se vedi come ho dichiarato l'operatore friend
   friend const MiaClasse operator+(int a, const MiaClasse& obj);
non faccio ritornare un reference all'operatore. Il reference lo uso nel
caso del passaggio degli argomenti dell'operatore. E in questo caso è lecito
perchè all'operatore passi un oggetto già creato, di cui ti interessa solo
per poter accedere ai suoi menbri e prelevarne i valori e poi chi si è visto
si è visto

 prassi di gran lunga piu' comune.
Direi che la regola generale dovrebbe essere: per reference si
restituisce solo un parametro che si e' ricevuto per reference. E'
consentito restituire per reference '*this' perche' di fatto e' un
caso particolare della regola generale.
Ciao,
Nicola

 anche di comodità per il programmatore?
si, e` possibile. Da nessuna parte e` scritto che l'operatore + deve 
essere commutativo, per cui il compilatore non puo` e non deve invertire 
da solo gli ordini degli operandi: pensa ad esempio al caso delle 
stringhe: e` definito un operator+ (che le concatena) ma esso *non* e` 
affatto commutativo; per risolvere puoi ricorrere ad un operator+ 
non-membro:
MiaClasse operator+(int, MiaClasse);
Mi raccomando, devi stare attento a inserire la funzione qua sopra nello 
stesso namespace in cui risiede MiaClasse, altrimenti avresti dei grossi 
problemi con la koening-lookup.
ciao Anto
-- 
"Computer science is not about computers any more than astronomy
is about telescopes." -- EW Dijkstra

 wrote in message
news:1t5k5b...@anto.cuni.lan...


caso particolare della regola generale.
E std::vector::back() ?
E std::map::operator[] ?
E tutte le funzioni che tornano un reference al singleton ?
E tutti gli altri casi ?
Ogni generalizzazione e' un errore ;)
Andrea

 Ogni generalizzazione e' un errore ;)
Mi sembra un po' eccessivo (la vedo la faccetta). Se così fosse non
potrebbero nascere le teorie. :-)


...

Solo se le viene dato piu' peso di quello che e' opportuno che abbia.
In effetti, per esempio, conviene trattare a parte il caso in cui si
restituisce un oggetto che e' aggregato a quello su cui abbiamo
chiamato la funzione.
Nota che da un punto di vista concettuale trovo il restituire per
reference in qesto caso appena meno debole del restituire per
reference un oggetto del quale non si conosce la provenienza. Se non
altro in questo caso le regole che controllano il lifetime
dell'oggetto restituito dovrebbero essere desumibili da quelle che
valgono per il suo contenitore.
Idealmente i contenitori dovrebbero essere distinti anche per le
modalita' di gestione del possesso del contenuto, e restituire i
propri elementi con meccanismi che riflettano tali modalita': ad
esempio un contenitore che possiede i propri elementi dovrebbe
restituirli tramite weak smart pointer.
Mi rendo conto pero' che nella pratica il ritorno per reference e' un
compromesso valido tra efficenza, semplicita' d'uso e di
implementazione, e sicurezza.
Resto pero' dell'idea che (possibilmente, per carita' :-) un oggetto
collegato ad un reference dovrebbe rimanere valido fintanto che il
reference e' valido e che non bisognerebbe restituire per reference se
non si e' in grado di soddisfare questo vincolo.
Ciao,
Nicola
P.S. Non mi piacciono i singleton, ed i singleton passati per
reference ancora meno.

reference ancora meno.
Neanche io sono un tifoso della classica implementazione
singleton con inizializzazione on-demand.
Come sempre capita la cosa ha pro e contro e al crescere
della complessita' i contro (per quello che e' la mia
esperienza) a volte si fanno sentire in modo prepotente.
Andrea

 fine del programma (e piu'). 
Infatti e' estremamente raro che io utilizzi reference come membri
dato, li uso raramente per ritornare valori da funzioni e li uso piu'
spesso come parametri formali di funzioni e come variabili locali.
Credo di comprendere il tuo discorso generale: tu dici e' inutile
attribuire ai costrutti del linguaggio un peso semantico che non e'
effettivamente supportato dal linguaggio; resta pero' il fatto che,
dal momento che si puo' quasi sempre scegliere un modo alternativo di
fare le cose, conviene cercare quello che esprime meglio le proprie
intenzioni.
Ciao,
Nicola