2010-10-08 12 views
8

Eventuali duplicati:
Where should non-member operator overloads be placed?overloading degli operatori e gli spazi dei nomi

Durante la navigazione su SO, spesso trovo domande o risposta che coinvolge sovraccarico/che definiscono un std::ostream& operator<<(std::ostream& os, const Foo& foo) o un Foo operator+(const Foo& l, const Foo& r).

Mentre so come e quando (non) scrivere questi operatori, sono confuso riguardo alla cosa namespace.

Se ho la seguente classe:

namespace bar 
{ 
    class Foo {}; 
} 

In quali namespace dovrei scrivere le diverse definizioni operatore?

// Should it be this 

namespace bar 
{ 
    std::ostream& operator<<(std::ostream& os, const Foo& foo); 
} 

// Or this ? 

namespace std 
{ 
    ostream& operator<<(ostream& os, const bar::Foo& foo); 
} 

// Or this ? 

std::ostream& operator<<(std::ostream& os, const bar::Foo& foo); 

La stessa domanda si applica allo operator+. Quindi, qual è la buona pratica qui e perché?

+1

duplicati di [Dove dovrebbe sovraccarichi dell'operatore non membro?] (http: // stackoverflow.it/questions/3623631/overloading-operator) –

+0

@James McNellis: Non sono riuscito a trovare una domanda simile. Grazie;) – ereOn

+1

Tieni presente che il secondo è in realtà illegale, puoi aggiungere solo specializzazioni allo spazio dei nomi 'std'. – GManNickG

risposta

9

Dovrebbe essere nel namespace bar. È necessario considerare what makes up the interface for the class e raggrupparli insieme.

"Una classe descrive un insieme di dati insieme alle funzioni che operano su tali dati." La tua funzione gratuita opera su un Foo, pertanto fa parte di Foo. Dovrebbe essere raggruppato con Foo nello spazio dei nomi bar.

Argument-dependent lookup o ADL, troverà la funzione.

Sappiamo anche che dovremmo prefer non-friend non-member functions. Ciò significa che, in generale, le classi avranno la loro definizione e le funzioni membro, seguite immediatamente da funzioni libere che operano sulla classe.

+0

Ha senso ed è anche facile da ricordare. Per un motivo oscuro, non ho considerato questi sovraccarichi come "funzioni libere" che sono in effetti. Grazie per le spiegazioni. – ereOn

12

La regola è che, quando si cerca un sovraccarico di funzione adatto, vengono considerati sia lo spazio dei nomi corrente che tutti gli spazi dei nomi delle definizioni del tipo di argomento. Questo è chiamato Argument Dependent Lookup (ADL).

Quindi, quando si dispone di questo codice:

::std::ostream& os = /* something */; 
    const ::bar::Foo& foo = /* something */; 
    os << foo; 

seguenti spazi dei nomi sono considerati:

  • Lo spazio dei nomi corrente
  • :: std, perché os tipo è definito ci
  • :: bar, perché il tipo di foo è definito lì

Quindi tutte e tre le possibilità che hai nominato funzioneranno e quindi sono "abbastanza buone" a prima vista.

Tuttavia ....

Non ti è permesso di definire nuove funzioni :: std, quindi non si può mettere il proprio operatore di sovraccarico in tale spazio dei nomi.(È consentito specializzare i modelli in :: std, ma non è quello che stiamo facendo qui)

In secondo luogo, lo "spazio dei nomi corrente" potrebbe cambiare, quindi se si inserisce la definizione della funzione in quello spazio dei nomi, potrebbe non essere sempre Essere trovato.

Così, alla fine, il posto migliore per mettere l'operatore overload è nello stesso namespace come Foo:

namespace bar 
{ 
    std::ostream& operator<<(std::ostream& os, const Foo& foo); 
} 
+0

Bello e preciso. Grazie. Ecco il mio +1. – ereOn

0

La scelta migliore è l'opzione 1. Perché? Perché quando si utilizza un nome di funzione non qualificato (un operatore sovraccarico è una funzione), oltre alla normale ricerca del nome, viene applicata la ricerca dipendente dall'argomento, ovvero (in modo informale) tutti gli spazi dei nomi in cui gli argomenti sono stati dichiarati vengono cercati. E.g.

namespace N 
{ 
    class X(){}; 
    void f(X){} 
} 
int main() 
{ 
    N::X x; 
    f(x); //works fine, no need to qualify f like N::f 
} 

Lo stesso vale per gli operatori.

D'altra parte, nel caso dell'opzione 2 l'operatore verrà comunque trovato perché ostream è in std (stessa regola ADL). Ma non è una buona idea aggiungere roba allo spazio dei nomi std.

E la terza opzione è male, stilisticamente - perché farlo se la prima opzione è sufficiente?

Quindi, sicuramente l'opzione 1.

HTH.

0

È buona norma dichiarare gli operatori (non membri) nello stesso spazio dei nomi della classe di cui fanno parte l'interfaccia.

Per qualcosa come operator+, questo è piuttosto semplice: opera solo su oggetti Foo, quindi dovrebbe andare nello stesso spazio dei nomi di Foo stesso. Per operator<< e operator>>, è comunque possibile scegliere tra gli spazi dei nomi std e bar. Prima di tutto, non è necessario aggiungere sovraccarichi di funzioni/operatori allo spazio nomi std. E in secondo luogo, la parte importante di questi sovraccarichi non è che funzionano con un flusso, ma che leggono/scrivono un oggetto Foo. Quindi ha più senso raggrupparlo con la classe Foo.

Va anche notato che le regole del C++ sono progettate in modo tale che gli operatori sovraccaricati che sono definiti nello stesso spazio dei nomi della classe su cui operano, saranno quasi sempre trovati correttamente, mentre questo andrà male molto più spesso se gli operatori sono dichiarati in un altro spazio dei nomi non correlato.

2

Per il corretto funzionamento dell'operatore, la funzione deve essere nello stesso spazio dei nomi di uno dei suoi operandi. Altrimenti, ADL non lo trova. Questo significa lo spazio dei nomi della classe per gli operatori come + e -. In teoria, è possibile inserire l'operatore < < in std o nello stesso spazio dei nomi della classe, ma lo standard vieta la definizione di nuove funzioni in std, quindi anche in questo caso, è inserirlo nello stesso spazio dei nomi della classe.

(E, naturalmente, non si solito implementare + o -, ma + = e - =, e quindi derivano da un modello che fornisce + e -. Automaticamente)