8

Si consideri il seguente codice:Un difetto nelle regole di risoluzione di sovraccarico C++?

#include <iostream> 

namespace ns1 
{ 
    struct A 
    { 
    }; 

    template <class T> 
    std::ostream& operator << (std::ostream& os, const T& t) 
    { 
     return os << "ns1::print" << std::endl; 
    } 
} 

namespace ns2 
{ 
    template <class T> 
    std::ostream& operator << (std::ostream& os, const T& t) 
    { 
     return os << "ns2::print" << std::endl; 
    } 

    void f (const ns1::A& a) 
    { 
     std::cout << a; 
    } 
} 


int main() 
{ 
    ns1::A a; 

    ns2::f (a); 

    return 0; 
} 

compilazione fallisce con "l'errore di sovraccarico ambiguo" secondo la norma.

Ma perché? Sicuramente l'operatore "ugualmente buono" nello spazio dei nomi "casa" di A dovrebbe avere la precedenza? C'è qualche ragione logica per non farlo?

+8

Perché pensi che le funzioni nello spazio dei nomi "casa" di 'A' dovrebbero avere la precedenza sulle funzioni nello spazio dei nomi della funzione chiamante' f' stessa? Non c'è modo di aggirare questo essere ambiguo. Un errore è l'unica cosa sensata. –

+0

Perché chi ha creato più spazio dei nomi sa come deve essere stampato A? – cppalex

+8

Prima di tutto, sono modelli. Se la persona che ha creato 'A' desiderava garantire un determinato comportamento per la stampa di oggetti di tipo' A', avrebbe fornito un sovraccarico o una specializzazione. Questo avrebbe risolto l'ambiguità qui. In secondo luogo, gli spazi dei nomi possono essere aperti e chiusi più volte, quindi la funzione potrebbe non essere stata fornita dall'implementatore di 'A'. –

risposta

10

Se si desidera che il sovraccarico in namespace A sia preferito, è necessario aggiungervi qualcosa per renderlo effettivamente migliore. Dire, rendendolo non un modello:

namespace ns1 
{ 
    std::ostream& operator<<(std::ostream&, const A&); 
} 

In caso contrario, non c'è davvero alcun motivo concettuale capire perché un modello di funzione in uno spazio dei nomi sarebbe preferibile ad un modello di funzione in un altro spazio dei nomi, se entrambi sono esattamente equivalenti. Dopo tutto, perché il modello di funzione nello spazio dei nomi di A è "migliore" del modello di funzione nello spazio dei nomi di f? L'implementatore di f non lo saprebbe "meglio"? Affidarsi esclusivamente alla firma della funzione aggira questo problema.

0

Se si legge con attenzione il compilatore-errori, l'errore di ambiguità non è tra le versioni in operator<<ns1 e ns2, ma tra il operator<<(os, const char*) esemplificazione dal ns1 e esattamente lo stesso di sovraccarico da namespace std. Quest'ultimo viene trascinato da ADL su std::ostream.

L'approccio migliore è quello di utilizzare la raccomandazione @Barry, e de-templatize il operator<< nel namespace ns1, ma anche per aggiungere tutte le funzionalità relative al ns1::A (come f(A)) nello stesso spazio dei nomi:

#include <iostream> 

namespace ns1 
{ 
    struct A {}; 

    std::ostream& operator << (std::ostream& os, const A& t) 
    { 
     return os << "ns1::print" << std::endl; 
    } 

    void f (const A& a) 
    { 
     std::cout << a; 
    }  
} 

int main() 
{ 
    ns1::A a; 
    f(a); // rely on ADL to find ns1::operator<<(os, A) 
} 

Live Example

namespace ns1 agisce quindi come l'interfaccia più ampia di class A attraverso ADL.

+1

Sì e no. Se si SFINAE le stringhe di caratteri, quindi 'cout << a' sarebbe ancora ambiguo tra i due modelli di funzione – Barry

+0

Sono tentato di semplificare l'esempio di OP in questo senso per cambiare l'operatore << (std :: ostream &, const T &) 'essere come' g (const T &) '. – Barry

+0

@Barry 'print()' qualcosa eliminerebbe tali sorprese, sì – TemplateRex