2017-03-22 4 views
2

I definiert eine Funktionsschablone in globalen Bereich alsWarum Operator << ist mehrdeutig?

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

Dann

std::cout << "\n"; 

nicht kompiliert wegen Mehrdeutigkeit. Ich denke, es ist Funktion Überladung und die Kompilierung kann es lösen, und wählen Sie die spezifischere Version für char const*. Warum kompiliert nicht?

+1

zeigen den vollständigen Fehler, werden die meisten Compiler die Alternativen Liste –

+1

Ich glaube, weil es bereits eine Standard-Überlastung für diese _exists_. – ForceBru

+1

lesen Sie diese http://stackoverflow.com/a/24088197/4743711 – Loreto

Antwort

3

Ihre Version von operator<< hier ist auf einem Argument templated, und so ist die Version, die durch den Standard geliefert wird.

Das Problem liegt darin, dass Ihre Version (wir es Funktion aufrufen, werden ein) auf ein Argument Templat ist:

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

und von cppreference, können Sie den Standard der Überlastung für const char* sehen (wir ll nennen es b) auf der anderen Templat:

template< class Traits > 
basic_ostream<char,Traits>& operator<<(basic_ostream<char,Traits>& os, 
             const char* s); 

Sie können das Verhalten erhalten Sie, indem Sie Ihre Version vonerwartendie gleiche Vorlage Argument für das erste Argument verwenden (dies ist c):

template<class T, class CharT, class Traits> 
inline std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os, T const& t) 
{  
     return os; 
} 

kompiliert glücklich, wie Sie on coliru sehen können.

Dies liegt daran, dass die Überladungsauswahl für die Vorlage die am meisten spezialisierte Vorlage auswählt, wenn eine Überladung mit mehreren möglichen Vorlagenfunktionen ausgewählt wird.

Insbesondere wird zuerst die Schablone transformiert:

[temp.func.order]:

  1. partielle Ordnung auswählt, welche von zwei Funktionsschablonen spezialisiertere als die andere ist durch Transformieren jeder Vorlage der Reihe nach (siehe nächsten Abschnitt) und Durchführen einer Vorlagenargumentableitung unter Verwendung des Funktionstyps. Der Deduktionsprozess bestimmt, ob eine der Templates spezialisierter ist als die andere. Wenn dies der Fall ist, ist die spezialisiertere Vorlage diejenige, die durch den partiellen Bestellprozess ausgewählt wird.

  2. Um die transformierte Vorlage für jeden Typ zu erzeugen, synthetisieren Nicht-Typ- oder Vorlagenparameter (einschließlich ihrer Template-Parameterpakete) einen eindeutigen Typ, Wert oder Klassenvorlage und ersetzen sie für jedes Vorkommen dieses Parameters im Funktionstyp der Vorlage. [Hinweis: Der Typ, der den Platzhalter im Typ des für einen nicht typisierten Vorlagenparameter synthetisierten Werts ersetzt, ist ebenfalls ein eindeutiger synthetisierter Typ. - Endnote] ...

[temp.deduct.partial]:

  1. Zwei Sätze von Arten verwendet werden, die teilweise Ordnung zu bestimmen. Für jede der beteiligten Vorlagen gibt es den ursprünglichen Funktionstyp und den transformierten Funktionstyp. [Hinweis: Die Erstellung des transformierten Typs wird in [temp.func.order] beschrieben. - Endnote] Der Deduktionsprozess verwendet den transformierten Typ als Argumentvorlage und den Originaltyp der anderen Vorlage als Parametervorlage. Dieser Prozess wird für jeden am partiellen Sortiervergleich beteiligten Typ zweimal durchgeführt: einmal die transformierte Vorlage-1 als Argumentvorlage und Vorlage-2 als Parametervorlage und erneut die transformierte Vorlage-2 als Argumentvorlage und Vorlage-1 verwenden als Parametervorlage.

  2. Die Typen verwendet, um die Reihenfolge zu bestimmen, von dem Kontext ab, in dem die partielle Ordnung erfolgt:

    1. Im Rahmen eines Funktionsaufrufs verwendeten Typen sind die Funktionsparameter Typen, für die die Funktion Aufruf hat Argumente.

...

  1. Funktionsvorlage F dest an wie als Funktionsvorlage G spezialisiert, wenn für jedes Paar von Arten verwendet, um die Reihenfolge zu bestimmen, die Art von F ist mindestens so groß wie die Art spezialisiert von G. F spezialisierter als G ist, wenn F dest auf als spezialisierte wie G und G nicht zumindest wie F. spezialisierte

so Wir wollen die Teile der Vorlagen, die die Funktionsargumente sind.

a) T     => std::ostream&,      T const& 
b) CharT, Traits  => std::basic_ostream<CharT, Traits>, const char* 
c) T, CharT, Traits => std::basic_ostream<CharT, Traits>, T const& 

Zum ersten Argument dieser Funktionen ist ein spezialisierter als b und c.

Für das zweite Argument dieser Funktionen, b ist spezialisierter als ein und c.

Wie wir in gelernt [temp.deduct.partial]/10 erfordert Argument Abzug aller relevanten Argumente einer Funktion f1 zu sein, zumindest als für die Funktion f1 als alle Argumente einer Funktion f2 spezialisiert mindestens so spezialisiert sein wie f2, ein kann hier nicht spezialisierter sein als b, da jedes ein Argument mehr spezialisiert als das andere passende Argument.

Dies ist nicht der Fall mit c, da beide ein und c alle Argumente zumindest als in c als Argumente spezialisiert.

Somit wird die Überladungsauflösung zwischen a und b weil bei der Auswahl eines mindestens so spezialisierte als b und b ist mindestens so spezialisiert wie a mehrdeutig sein. Die Mehrdeutigkeit wird durch die Verwendung c entfernt statt ein weil b mindestens so spezialisiert wie c, aber das Gegenteil ist nicht wahr.

Wie wir wissen von [temp.func.order]/2, die mehr spezialisierte Funktion gewinnt Überladungsauflösung, und mit der Verwendung von c statt ein, der Gewinner ist b und die Leitung std::cout << "hello"; druckt hello auf die Konsole.

+0

"* Sie können das Verhalten erhalten, das Sie erwarten, indem Sie Ihre Version des Operators << für das erste Argument das gleiche Template-Argument verwenden" - sollte der Rückgabewert nicht auch templated sein, um dem Argument zu entsprechen? 'template inline std :: basic_ostream & operator << (std :: basic_ostream & os, T const & t)'. 'std :: ostream' ist eine Spezialisierung von' std :: basic_ostream' für 'char', aber was ist, wenn' CharT' nicht 'char' ist? –

+0

@RemyLebeau yep, danke für den Fang (obwohl es eigentlich die Überladungsauflösung hier nicht ändert. Spaß mal.) – jaggedSpire

+0

Ich denke das ist es. Ich könnte den vollständigen Satz von Regeln auflisten, die definieren, wie Parameter als spezialisierter bestimmt werden, aber das ist etwas übertrieben, wenn man bedenkt, dass es sich um relativ einfache Fälle handelt, die lang sind (und leicht über die Links zum Standard entdeckt werden können). – jaggedSpire

0

Es gibt eine Funktion Vorlage

template <class Traits> 
... operator<<(basic_ostream<char, Traits> &, const char *); // @1 

in der Standardbibliothek erklärt.

Nach Substitution, die Umwandlung erforderliche Standard Spezialisierung und Sie nennen, auch bekannt als

template<class T> 
... operator<<(std::ostream& os, T const& t); // @2 

ist sowohl als genaue Übereinstimmung gewählt.

Dann wird der Compiler versuchen, diesen Aufruf von partial ordering rules zu lösen:

@1 from transformed @2 : 
(basic_ostream<char, T> &, const char *) from (std::ostream& os, U const& t) 
: P2 = const char *, A2 = U const & : deduction fails 

@2 from transformed @1 : 
(std::ostream& os, T const& t) from (basic_ostream<char, U> &, const char *) 
: P1 = std::ostream&, A1 = basic_ostream<char, U> & : deduction fails 

somit weder speziellere, der Anruf nicht eindeutig ist.


Es gibt einige andere Kandidaten:

operator<<(basic_ostream<_CharT, _Traits>&, const char*) 
operator<<(basic_ostream<_CharT, _Traits>&, const _CharT*) 

Aber leider, sie beide sind weniger spezialisiert als der große Bruder @ 1 in diesem Fall. Also habe ich sie rausgeschmissen ...

Und einige mehr, aber diese beiden sind nur für die weniger lesbare Fehlermeldung.

operator<<(bool __n) 
operator<<(const void* __p)