8

Betrachten Sie den folgenden Code ein:Ein Fehler in C++ - Überladungsauflösungsregeln?

#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; 
} 

Compilation nicht mit "mehrdeutig Überlastung Fehler" gemäß der Norm.

Aber warum? Sicherlich sollte der "gleich gute" Operator im Heimat-Namespace von A Vorrang haben. Gibt es einen logischen Grund, es nicht zu tun?

+8

Warum sollten Funktionen im Home-Namespace von "A" Vorrang vor Funktionen im Namespace der aufrufenden Funktion "f" haben? Es gibt keinen Weg um dieses mehrdeutig zu sein. Ein Fehler ist das einzig Vernünftige. –

+0

Weil derjenige, der als Namespace erstellt hat, besser weiß, wie A gedruckt werden soll? – cppalex

+8

Zunächst einmal sind sie Vorlagen. Wenn die Person, die "A" erstellt hat, ein bestimmtes Verhalten für das Drucken von Objekten des Typs "A" sicherstellen wollte, hätte dies entweder eine Überladung oder Spezialisierung bewirkt. Das hätte die Zweideutigkeit hier gelöst. Zweitens können Namespaces mehrere Male geöffnet und geschlossen werden, so dass die Funktion möglicherweise nicht einmal von dem Implementierer von "A" bereitgestellt wurde. –

Antwort

10

Wenn Sie die Überladung in namespace A bevorzugen möchten, müssen Sie etwas hinzufügen, um es tatsächlich besser zu machen. Sprich, indem sie sie nicht eine Vorlage machen:

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

Ansonsten gibt es wirklich keinen konzeptionellen Grund zu sehen, warum ein Funktions-Template in einem Namensraum zu einer Funktionsvorlage in einem anderen Namensraum bevorzugt werden würde, wenn beide genau gleich sind. Warum sollte die Funktionsvorlage im Namespace von A "besser" sein als die Funktionsvorlage im f Namespace? Würde der Implementierer von f "besser wissen"? Die ausschließliche Verwendung der Funktionssignatur verhindert dieses Problem.

0

Wenn Sie sorgfältig die Compiler-Fehler zu lesen, sind die Mehrdeutigkeit Fehler nicht zwischen den operator<< Versionen in ns1 und ns2, sondern zwischen der operator<<(os, const char*) Instanziierung von ns1 und die exakt gleichen Überlastung von namespace std. Letzteres wird von ADL unter std::ostream eingezogen.

Der beste Ansatz ist die Empfehlung von @Barry und de-templatize der operator<< im Namensraum ns1, zu verwenden, sondern auch alle Funktionen hinzufügen, um ns1::A (wie f(A)) bezogen in den gleichen Namensraum:

#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) 
} 
ns1

Live Example

Namespace dann wirkt, wenn die allgemeine Schnittstelle von class A durch ADL.

+1

Ja und nein. Wenn Sie die Zeichenketten SFINAE herausnehmen, dann wäre 'cout << a 'immer noch mehrdeutig zwischen den beiden Funktionsvorlagen. – Barry

+0

Ich bin versucht, das Beispiel von OP in dieser Hinsicht zu vereinfachen, um' operator 'zu ändern (std :: ostream &, const T &) 'wie' g (const T &) 'zu sein. – Barry

+0

@Barry 'print()' etwas würde solche Überraschungen beseitigen, ja – TemplateRex