8

Rozważmy następujący kod:Usterka w zasadach dotyczących przeciążenia 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; 
} 

Kompilacja nie powiedzie się z „błąd przeciążenia niejednoznacznym”, jak na standard.

Ale dlaczego? Z pewnością "równie dobry" operator w przestrzeni nazw "A" powinien mieć pierwszeństwo? Czy istnieje jakiś logiczny powód, aby tego nie robić?

+8

Dlaczego według Ciebie funkcje w przestrzeni nazw 'A' home powinny mieć pierwszeństwo przed funkcjami w przestrzeni nazw funkcji wywołującej' f'? Nie ma sposobu, aby to było niejednoznaczne. Błąd jest jedyną rozsądną rzeczą. –

+0

Bo kto stworzył niż przestrzeń nazw wie lepiej, jak należy wydrukować A? – cppalex

+8

Przede wszystkim są szablonami. Jeśli osoba, która utworzyła "A", chciała zapewnić pewne zachowanie dla drukowania obiektów typu "A", zapewniłaby albo przeciążenie, albo specjalizację. To rozwiązałoby tu niejednoznaczność. Po drugie, przestrzenie nazw można wielokrotnie otwierać i zamykać, więc funkcja ta mogła nie zostać dostarczona przez implementatora 'A'. –

Odpowiedz

10

Jeśli chcesz, aby przeładowanie w namespace A było preferowane, musisz dodać coś, aby było lepiej. Powiedzieć, czyniąc go nie szablon:

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

przeciwnym razie istnieje naprawdę nie ma powodu, aby zobaczyć koncepcyjny dlaczego szablon funkcji w jednej przestrzeni nazw będzie wolał szablonu funkcji w innej przestrzeni nazw, jeśli oba są dokładnie równoważne. W końcu dlaczego szablon funkcji w przestrzeni nazw A byłby "lepszy" niż szablon funkcji w przestrzeni nazw f? Czy realizator z f nie "wie lepiej"? Opieranie się wyłącznie na sygnaturze funkcji omija ten problem.

0

Jeśli uważnie przeczytać kompilator błędy, błąd nie jest niejasność pomiędzy wersjami operator<< w ns1 i ns2, ale między operator<<(os, const char*) instancji z ns1 i dokładnie w tym samym przeciążenia od namespace std. Ten ostatni jest wleczony przez ADL pod numerem std::ostream.

Najlepszym rozwiązaniem jest skorzystanie z rekomendacji przez @Barry i de-templatize operator<< w przestrzeni nazw ns1, ale także, aby dodać wszystkie funkcje związane z ns1::A (takich jak f(A)) w tej samej przestrzeni nazw:

#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

nazw ns1 działa wówczas jako szerszy interfejsu class A przez ADL.

+1

Tak i nie. Jeśli SFINAE out ciągi znaków, to "cout << a" nadal będzie niejednoznaczne między dwoma szablonami funkcji – Barry

+0

Mam ochotę uprościć przykład OP w tym względzie, aby zmienić 'operator << (std :: ostream &, const T &) 'byc jak' g (const T &) '. – Barry

+0

@Barry 'print()' coś wyeliminowałoby takie niespodzianki, tak – TemplateRex