Dieses Problem ist ein wenig schwer zu erklären, so dass ich mit einem Beispiel an:Template Spezialisierung für Subklassen von Template-Basisklasse
ich eine Klassenvorlage, die einen Typ und eine Integer-Konstante als Template-Parameter nimmt, und ich habe eine Reihe von Kinderklassen, die von Instantiierungen dieser Vorlage ableiten:
template <class V, int i>
struct Base
{
static void doSomething() { cout << "something " << i << endl; };
};
struct Child : public Base<int,12>
{
};
ich mag diese Klassen mit einer anderen Vorlage verwenden (sie es Testanruf), die Spezialisierungen für verschiedene Arten hat. Da das Verhalten für alle Klassen, die von einer Instanz von Base abgeleitet werden, genau gleich sein soll, möchte ich nur eine Spezialisierung von Test definieren, die alle von Base abgeleiteten Klassen behandelt.
Ich weiß, dass ich nicht direkt für Basis < V, ich > spezialisieren kann, weil dies die untergeordneten Klassen nicht erkennen würde. Stattdessen wurde mein erster Ansatz Erhöhung der enable_if und Art Züge mit:
// empty body to trigger compiler error for unsupported types
template <class T, class Enabled = void>
struct Test { };
// specialization for ints,
// in my actual code, I have many more specializations here
template <class Enabled>
struct Test <int, Enabled>
{
static void test (int dst)
{
cout << "Test<int>::test(" << dst << ")" << endl;
}
};
// this should handle all subclasses of Base,
// but it doesn't compile
template <class T, class V, int i>
struct Test <T, typename enable_if <is_base_and_derived <Base <V,i>, T>>::type>
{
static void test (const T &dst)
{
dst.doSomething();
}
};
int main (int argc, char **argv)
{
Test <int>::test (23);
Test <Child>::test (Child());
return 0;
}
Die Idee war, dass die Spezialisierung alle Klassen behandeln soll, die von Basis mit irgendwelchen beliebigen Werten von V und i abgeleitet werden. Das funktioniert nicht, gcc klagt:
error: template parameters not used in partial specialization: error: ‘V’ error: ‘i’
Ich denke, das Problem ist, dass dieser Ansatz die Compiler erfordern würde, alle möglichen Kombinationen von V zu versuchen, und mir zu überprüfen, ob einer von ihnen übereinstimmt. Vorerst arbeitete ich, um das Problem durch etwas auf die Basisklasse hinzu:
template <class V, int i>
struct Base
{
typedef V VV;
static constexpr int ii = i;
static void doSomething() { cout << "something " << i << endl; };
};
Auf diese Weise, die Spezialisierung nicht mehr braucht V haben und ich als freie Template-Parameter:
template <class T>
struct Test <T, typename enable_if <is_base_and_derived <Base <typename T::VV, T::ii>, T>>::type>
{
static void test (const T &dst)
{
dst.doSomething();
}
};
Und dann es kompiliert.
Nun ist meine Frage: Wie kann ich das tun, ohne die Basisklasse zu ändern? In diesem Fall war es möglich, weil ich es selbst geschrieben habe, aber was kann ich tun, wenn ich in meiner Testvorlage mit dem Bibliothekskode von Drittanbietern umgehen muss? Gibt es eine elegantere Lösung?
Edit: Kann mir auch jemand eine detaillierte Erklärung geben, warum genau der erste Ansatz nicht funktioniert? Ich habe eine grobe Vorstellung, aber ich würde es vorziehen, ein richtiges Verständnis zu haben. :-)
Danke für den Base_Base-Tipp, der meinen aktuellen Code etwas lesbarer macht. Bei Bibliotheken von Drittanbietern funktioniert dies jedoch nicht, zumindest nicht, wenn die untergeordneten Klassen ebenfalls zur Bibliothek gehören. –
@BenjaminSchug: vielleicht [this] (http://stackoverflow.com/a/6398983/1324131) beantwortet Ihre neue Frage in der bearbeiteten Frage. – user2k5
Danke, das erklärt, warum der erste Ansatz nicht funktioniert hat.Scheint so, als wäre mein Bauchgefühl richtig. –