2010-02-25 15 views
37

Ho una domanda sul valore di ritorno dell'overloading dell'operatore in C++. In generale, ho trovato due casi, uno è di ritorno per valore e uno è di ritorno per riferimento. Quindi qual è la regola sottostante? Soprattutto nel caso in cui è possibile utilizzare l'operatore in modo continuo, ad esempio cout<<x<<y.valore di ritorno del sovraccarico dell'operatore in C++

Ad esempio, quando si implementa un'operazione + "stringa + (stringa)". come vorresti restituire il valore di ritorno, per ref o per val.

risposta

56

Alcuni operatori restituiscono per valore, alcuni per riferimento. In generale, un operatore il cui risultato è un nuovo valore (come +, -, ecc) deve restituire il nuovo valore per valore e un operatore il cui risultato è un valore esistente, ma modificato (ad esempio < <, >>, + =, - =, ecc), dovrebbe restituire un riferimento al valore modificato.

Ad esempio, cout è un std::ostream, e l'inserimento dei dati nel flusso è un'operazione modifica, in modo da implementare all'operatore << da inserire in un ostream, l'operatore è definito così:

std::ostream& operator<< (std::ostream& lhs, const MyType& rhs) 
{ 
    // Do whatever to put the contents of the rhs object into the lhs stream 
    return lhs; 
} 

Questo modo, quando si dispone di un'istruzione composta come cout << x << y, viene valutata prima la sottoespressione cout << x, quindi viene valutata l'espressione [result of cout << x ] << y. Poiché l'operatore << su x restituisce un riferimento a cout, l'espressione [result of cout << x ] << y equivale a cout << y, come previsto.

Al contrario, per "stringa + stringa", il risultato è una nuova stringa (entrambe le stringhe originali sono invariate), quindi deve restituire per valore (altrimenti si restituirebbe un riferimento a un temporaneo, che è un comportamento non definito) .

+9

Che va di pari passo con "se è const, restituisce un valore, se non è const, restituisce un riferimento". – GManNickG

+10

Tecnicamente '<<' non modifica il valore esistente. È solo un operatore del turno di sinistra. È l'interfaccia del flusso che infrange le regole. – kennytm

+0

@GMan corretto, anche se questo non copre i casi (come 'operatore <<') dove il sovraccarico viene implementato come non membro. –

2

A seconda dell'operatore, potrebbe essere necessario restituire in base al valore.

Quando entrambi possono essere utilizzati anche se, come in operatore + = si potrebbe considerare il seguente:

  • Se gli oggetti sono immutabili è probabilmente meglio tornare per valore.
  • Se i tuoi oggetti sono mutabili probabilmente è meglio tornare per riferimento.
+0

Lo scopo di 'operator + =' è di mutare l'oggetto. Non dovrebbe essere chiamato su oggetti immutabili. – army007

2

Di solito si restituisce per riferimento in un'operazione che modifica il valore delle cose su cui sta operando, come = o +=. Tutte le altre operazioni sono restituite in base al valore.

Questa è più una regola empirica, però. Puoi progettare il tuo operatore in entrambi i modi.

4

Se si desidera che il sovraccarico dell'operatore si comporti come l'operatore integrato, la regola è piuttosto semplice; lo standard definisce esattamente come si comportano gli operatori integrati e indica se il risultato di un built-in è uno rvalue o uno lvalue.

La regola si dovrebbe usare è:

  • se il built-in operatore restituisce un rvalue allora il vostro sovraccarico dovrebbe restituire un riferimento
  • se il built-in restituisce un lvalue allora il vostro sovraccarico dovrebbe restituire un valore

Tuttavia, il sovraccarico non è necessario per restituire lo stesso tipo di risultato dell'integrato, anche se è ciò che si dovrebbe fare a meno che non si abbia una buona ragione per fare diversamente. Ad esempio, KennyTM ha notato in un commento ad un'altra risposta che, ad esempio, KennyTM ha rilevato che gli stream overload per gli operatori << e >> restituiscono un riferimento all'operando di sinistra, che non è il modo in cui funzionano i built-in. Ma i progettisti dell'interfaccia di flusso hanno fatto in modo che l'I/O del flusso potesse essere incatenato.

+0

Per riferimento gli operatori integrati si trovano nel § 13.6 dello standard C++ 11 della bozza N3337, che è l'unica versione dello standard che ho a portata di mano. –

12

Per tentare una risposta alla domanda relativa alle stringhe, l'operatore +() per le stringhe viene quasi sempre implementato come una funzione libera (non membro) in modo che le conversioni implicite possano essere eseguite su entrambi i parametri. Questo è così si può dire le cose come:

string s1 = "bar"; 
string s2 = "foo" + s1; 

Dato che, e che possiamo vedere che né parametro può essere modificato, deve essere dichiarato come:

RETURN_TYPE operator +(const string & a, const string & b); 

ignoriamo il return_type per la momento. Poiché non siamo in grado di restituire uno dei parametri (perché non possiamo cambiarli), l'attuazione deve creare un nuovo valore concatenato:

RETURN_TYPE operator +(const string & a, const string & b) { 
    string newval = a; 
    newval += b; // a common implementation 
    return newval; 
} 

Ora, se facciamo return_type un punto di riferimento, ci torneremo un riferimento a un locale oggetto, che è un no-no noto in quanto l'oggetto locale non esiste al di fuori della funzione. Quindi la nostra unica scelta è di restituire un valore, ad esempio una copia:

string operator +(const string & a, const string & b) { 
    string newval = a; 
    newval += b; // a common implementation 
    return newval; 
} 
+0

Quindi, per liberare il risultato assegnato, sono corretto che poiché il richiamo dell'operatore è in effetti una dichiarazione, il valore di ritorno anonimo va in pila, e quindi viene liberato quando la funzione di contenimento ritorna? –

Problemi correlati