Vererbung innerhalb einer generischen Klasse
-
Hallo Zusammen,
wenn ich innerhalb einer generischen Klasse zwei weitere Klassen lokal definiere, wobei die eine von der anderen eine "protected Element-variable" erbt, kann von dieser aus nicht auf die Variable der Basisklasse zugegriffen werden. In einer nicht generischen Klasse funktioniert dies aber.
Was mache ich falsch? Liegt das am Compiler(Dev-C++)?Hier der Code:
template <class Type> //Ohne diese Zeile funktioniert es. class Hauptklasse { public: class Basis { protected: int a; }; class Abgeleitet : public Basis { public: void test() { a; //Fehler: "'a' undeclared" tritt nur bei generischer Hauptklasse auf. } }; };Hoffe auf eine Lösung.
Danke im VorausGruss ganesh
-
template <class Type> class Hauptklasse { public: class Basis { protected: int a; }; class Abgeleitet : public Hauptklasse<Type>::Basis { public: void test() { this->a; } }; };Das liegt da dran, dass Hauptklasse noch nicht instanziert wurde, daher existiert auch kein Basis. Daher muss man genau sagen, was man fur eine Basisklasse möchte (Ziele 11).
Weiterhin existiert aus dem gleichen Grund die Basisklasse noch nicht, wenn der Code in Zeile 16 geprüft wird, daher muss man auch genauer sagen, was das a sein soll. Also entweder
Hauptklasse<Type>::Basis::a;(ein typedef drängt sich auf
) oder wie hier this->a;, damit klar ist, dass es sich um einen Member unserer Klasse handeln soll.Später, falls die Klasse mal tatsächlich instanziert wird, werden diese Bedingungen dann geprüft.
-
Der Compiler hat recht, die Begründung ist (in diesem Fall) etwas unintuitiv.
Während des unqualifizierten Lookups von nicht-abhängigen Bezeichnern werden abhängige (gemeint ist abhängig von den den Templateargumenten) Basisklassen nicht durchsucht, weil ja erst bei der Instantiierung kalr ist, welche konkrete Spezialisierung zu benutzen ist, die Bedeutung des Bezeichners kann ja zwischen verschiedenen Spezialisierungen variieren. Das ist - wenn man schon ein wenig mit Templates gearbeitet hat - nichts Neues.
Ein Bezeichner ist aber tyischerweise nicht abhängig, wenn er auf ein Member der gegenwärtigen Instantiierung verweist (auch nichts Neues, andernfalls würde eine Menge Code völlig unleserlich werden).
z.B.int x; template <typename T> class foo { static T x; static const auto s = sizeof(x); // gemeint ist foo<T>::x , nicht das äußere x };Gegenwärtige Instantiierung bezieht sich aber nur auf auf den gegenwärtigen Member oder die gegenwärtige verschachtelte Klasse oder die Templateklasse selbst, nicht aber auf andere verschachtelte Klassen, auch wenn sieht zur gleichen Templateklasse gehören.
template <typename T> class foo { class X; class Y { // hier ist X abhängig }; class X : Y { //hier ist Y abhängig }; };Nachdem also klar ist, dass Basis::a an der Stelle ein abhängiger Bezeichner ist, muss der Ausdruck nur noch so umgeformt werden, dass es sich um einen abhängigen Ausdruck handelt.
Dafür gibt es wieder mehrere Möglichkeiten, in der Regel wird man eine der ersten beiden Varianten verwendenBasis::a; // ein qualifizierter Name ist abhängig, wenn der Namensraum (d.h. der Typ) abhängig ist this->a; // this ist immer abhängigMan kann noch mehr finden, z.B.
template <typename T> struct identity { typedef T type; }; Hauptklasse<typename identity<T>::type>::Abgeleitet::a; //kann in Templatecode nützlich sein, wenn die Basisklasse schwer aufzuschreiben ist, und this nicht verwendet werden kann
-
Ich fürchte, da muss ich dir widersprechen, Sepp. Das Problem ist nicht, dass die Basisklasse nicht genau benannt wird -- die kann der Compiler aus dem Kontext herleiten -- sondern esoterische Lookup-Regeln für Templates.
So geht's:
template <class Type> //Ohne diese Zeile funktioniert es. class Hauptklasse { public: class Basis { protected: int a; }; class Abgeleitet : public Basis { using Basis::a; // <-- Hier public: void test() { a; } }; };Die Basisklasse wird für die Namensauflösung an dieser Stelle sonst schlicht nicht herangezogen. Das hängt damit zusammen, dass Hauptklasse keine Klasse, sondern eine Klassenvorlage ist, wodurch Basis und Abgeleitet es auch werden. Man dachte dabei wohl an Fälle wie diesen:
#include <iostream> void foo() { std::cout << "Hallo, Welt.\n"; } template <class Type> //Ohne diese Zeile funktioniert es. class Hauptklasse { public: class Basis { }; class Abgeleitet : public Basis { public: void test() { foo(); } }; }; template<> class Hauptklasse<int>::Basis { protected: void foo() { std::cout << "Hähä, ich böser Bursche!\n"; } }; int main() { Hauptklasse<double>::Abgeleitet abg2; Hauptklasse<int>::Abgeleitet abg; abg.test(); abg2.test(); }
-
Danke für die raschen Lösungen und Erklärungen!
Gruss ganesh123