Ja, es ist ein wenig überraschend, aber Vererbung und Vorlage mischen sich nicht so gut, wenn es um Überladungsauflösung geht.
Die Sache ist, wenn der Compiler ausgewählt wird, welche Überladung ausgewählt werden soll, wählt der Compiler diejenige aus, die die wenigsten Konvertierungen benötigt (eingebaut in eingebaute, abgeleitete zu base, Aufrufe zu nicht expliziten Konstruktoren oder Konvertierungsoperatoren) , etc...). Der Ranking-Algorithmus ist eigentlich ziemlich komplex (nicht alle Konvertierungen werden gleich behandelt ...).
Sobald die Überladungen geordnet sind, wird die Vorlage verworfen, wenn die beiden obersten gleichrangig sind und einer eine Vorlage ist.Wenn die Vorlage jedoch einen höheren Rang als die Nichtvorlage aufweist (normalerweise weniger Conversions), wird die Vorlage ausgewählt.
In Ihrem Fall:
- für
std::vector<int>
nur eine Überlastung Streichhölzer, so wird es ausgewählt.
- für
A
zwei Überladungen übereinstimmen, sie gleichen gleichermaßen, die Vorlage wird verworfen.
- für
B
zwei Überladungen übereinstimmen, der Template-Rang höher (keine Umwandlung von abgeleiteten in Basis erforderlich), wird ausgewählt.
Es gibt zwei Workarounds, ist die einfachste zu „reparieren“ die Aufrufort:
A const& ba = b;
foo(ba);
Die andere ist die Vorlage selbst zu beheben, aber das ist kniffliger ...
Sie, dass für die Klassen codieren von A
abgeleitet ist dies nicht die Überlastung Sie wünschen:
template <typename T>
typename std::enable_if<not std::is_base_of<A, T>::value>::type
foo(T const& t) {
std::cerr << "Generic case\n";
}
Doch diese flexib nicht so ist le ...
Eine andere Lösung besteht darin, einen Haken zu definieren. Zuerst müssen wir einige metaprogramming Dienstprogramm:
// Utility
template <typename T, typename Result = void>
struct enable: std::enable_if< std::is_same<T, std::true_type>::value > {};
template <typename T, typename Result = void>
struct disable: std::enable_if< not std::is_same<T, std::true_type>::value > {};
Und dann definieren wir unsere Haken und Funktion:
std::false_type has_specific_foo(...);
template <typename T>
auto foo(T const& t) -> typename disable<decltype(has_specific_foo(t))>::type {
std::cerr << "Generic case\n";
}
Und dann für jede Basisklasse wollen wir eine spezifische foo:
std::true_type has_specific_foo(A const&);
In Aktion bei ideone.
Es ist auch in C++ 03 möglich, aber etwas mühsamer. Die Idee ist dieselbe, ein Ellipsenargument ...
hat den schlechtesten Rang, also können wir die Überladungsauswahl für eine andere Funktion verwenden, um die Wahl des primären zu steuern.
ja - nur die eine Methode vereinfacht die Dinge. Ich vermute, dass ich erwartet habe, dass der const B & call auch in diesem Fall fehlschlägt. – ATemp
'B' ist ein direktes Kind von' A'. Es kann an ein 'A &' oder 'const A &' gebunden werden. – pmr
Und jetzt merke ich, ich habe 31 Minuten damit verbracht, meine Antwort zu verfassen ... SO isst meine Zeit: x –