5

Es gelten folgende Funktionsschablonen:Warum wählt der Compiler im folgenden Beispiel nicht meine Überladung von Funktionsvorlagen aus?

#include <vector> 
#include <utility> 

struct Base { }; 
struct Derived : Base { }; 

// #1 
template <typename T1, typename T2> 
void f(const T1& a, const T2& b) 
{ 
}; 

// #2 
template <typename T1, typename T2> 
void f(const std::vector<std::pair<T1, T2> >& v, Base* p) 
{ 
}; 

Warum ist es, dass der folgende Code Überlastung # 1 statt Überlastung # 2 immer ruft?

int main() 
{ 
    std::vector<std::pair<int, int> > v; 
    Derived derived; 

    f(100, 200); // clearly calls overload #1 
    f(v, &derived);   // always calls overload #1 

    return 0; 
} 

Da der zweite Parameter von f eine Art von Base abgeleitet ist, ich habe gehofft, dass die Compiler Überlastung # wählen würde, 2, da es eine bessere Übereinstimmung als die Gattung Überlastung # 1 ist.

Gibt es irgendwelche Techniken, die ich verwenden könnte, um diese Funktionen neu zu schreiben, so dass der Benutzer Code wie in der main-Funktion angezeigt schreiben kann (d. H. Mithilfe von Compiler-Abzug von Argumenttypen)?

+0

Obwohl es nichts mit der Hauptfrage zu tun hat, immer noch: 1. 'int main()', bitte. 2. Auf Ihre Vorlagenfunktionsdefinitionen folgen leere Deklarationen ';'. Dies ist in C++ illegal. – AnT

Antwort

12

Sie entweder das tun können:

f(v, static_cast<Base*>(&derived)); 

Oder nutzt SFINAE die ersten entfernen Funktion als Auswahlkandidat:

// Install boost library and add these headers: 
#include <boost/utility/enable_if.hpp> 
#include <boost/type_traits.hpp> 

// #1 - change it to look like this (note the keyword void changed positions) 
template <typename T1, typename T2> 
typename boost::disable_if< 
    typename boost::is_convertible<T2, Base*>, void>::type 
f(const T1& a, const T2& b) 
{ 
}; 

// #2 - this one can stay the same 
template <typename T1, typename T2> 
void f(const std::vector<std::pair<T1, T2> >& v, Base* p) 
{ 
}; 
+0

Boost zur Rettung! Sehr schlau! –

+0

Dang Ich habe nur das hinzugefügt. :) – GManNickG

+0

Die einzige traurige Anmerkung ist, dass es so viel weniger lesbar ist ...+1 sowieso, erklären ist gut, eine Lösung vorzuschlagen ist noch besser :) –

8

Da der zweite Parameter von f eine abgeleitete Basentyp

Es ist umwandelbar derart ist, aber es wird * a abgeleitet. Die erste Template-Funktion benötigt keine Conversions und die zweite benötigt eine, daher wählt sie die erste.

Dies wählt die zweite:

f(v, static_cast<Base*>(&derived)); 

Auf einer Seite zur Kenntnis, main ein int zurückgibt.

+0

Vielen Dank für die Erklärung. Ich habe den Rückgabewert von main entfernt, da es im Beispiel nicht notwendig war. –

+0

Nun main hat eine implizite 'return 0;' so kannst du int trotzdem lassen :) Und es ist ein Zeichen kürzer! : P – GManNickG

+3

Notwendig oder nicht, C++ - Sprache _requires_ dass 'main' mit dem Rückgabetyp 'int' deklariert wird. – AnT

1

Neben den offensichtlichen Themen über Koenig Lookup, die mehr oder weniger gut von Compilern implementiert werden (vor allem ältere sind ziemlich problematisch), gibt es ein paar Fallstricke in Bezug auf template specialization.

Die Spezialisierung erfordert, dass die Typen genau übereinstimmen (nicht sicher, wie die Std dies definiert, aber aus meiner Erfahrung [gcc, msvc] wird eine abgeleitete Klasse nicht gefunden). Wenn Sie eine hässliche Stich Basis * hinzufügen, sollte es funktionieren, wie Sie beabsichtigen, gegebenenfalls eine weitere Spezialisierung hinzufügen für Abgeleitet ...