Verbung von typdefinitionen in Template-Klassen
-
Hi,
warum geht das folgende nicht?
template <class T> class A { public: typedef T my_internal_type; ... }; template <class T> class B : public A<T> { public: B(my_internal_type x) {..} };B kennt my_internal_type nicht - sollte B den nicht kennen
?
Danke fuer Hinweise
-
Vererber schrieb:
B kennt my_internal_type nicht - sollte B den nicht kennen
?Nein! B sollte die Typendefinition nicht kennen.
So gehts:
template <class T> class B : public A<T> { public: typedef A< T >::my_internal_type my_internal_type; B(my_internal_type x) {..} };
-
Hallo,
das Problem ist das Selbe wie hier: http://www.c-plusplus.net/forum/viewtopic-var-t-is-170395.htmlNochmal kurz zusammengefasst:
Ein standardkonformer Compiler versucht Templates so früh und soweit wie möglich zu prüfen. D.h. er versucht bereits die Definition des Templates zu checken, noch bevor das Template instanziiert wird. Da zu diesem Zeitpunkt allerdings noch nicht alle Informationen für einen vollständigen Check zur Verfügung stehen, teilt der Compiler die Template-Prüfung in zwei Phasen auf. In der ersten Phase (vor der Instanziierung) prüft der Compiler die generelle Syntax, sowie alle Namen, die nicht von einem Template-Parameter abhängig und somit also unabhängig von der konkreten Instanziierung sind (wichtig!). Die zweite Phase beginnt, wenn das Template instanziiert wird. In dieser Phase werden alle restlichen Namen geprüft.Soviel als Einführung. Jetzt zu dem konkreten Problem: Normalerweise sieht eine abgeleitete Klasse alle Namen, die in ihren Basisklassen deklariert worden sind.
Ausnahme: Handelt es sich bei der Basisklasse um ein Template und ist dieses Template von mindestens einem Template-Parameter der abgeleiteten Klasse abhängig, so stehen die Namen der Basisklasse *nicht* implizit in abgeleiteten Klasse zur Verfügung.
Der Grund für diese Ausnahme ist das "two-phase-lookup"-Prinzip zusammen mit der Tatsache, dass Templates spezialisiert werden können.
Der Name my_internal_type ist so erstmal ein unabhängiger Name (d.h. so wie er da steht, kann man nicht erkennen, dass er von einem Template-Parameter abhängt). Nach den "two-phase-lookup"-Regeln muss dieser Name also bereits in der ersten Phase überprüft (und gebunden) werden. Angenommen es gäbe die oben erwähnte Ausnahme nicht und der Compiler könnte den Namen in der abhängigen Basisklasse nachschauen. In diesem Fall bekäme er ein Problem, falls verschiedene Spezialisierungen der Basisklasse dem Namen verschiedene Bedeutungen zuordnen:
template <class T> class A { public: typedef T my_internal_type; }; template <class T> class A<T*> { public: typedef T my_internal_type; }; template <class T> class B : public A<T> { public: // Ups! Was ist my_internal_type? -> Keine Ahnung, abhängig von T! B(my_internal_type x) {..} };Um dieses Problem zu umgehen, wurde die oben erwähnte Ausnahme eingeführt. D.h. Namen wie my_internal_type werden *nicht* in abhängigen Basisklassen nachgeschaut, sondern nur im Scope der Klasse und in darüberliegenden Namespace-Scopes. Da es in diesen Scopes kein Element mit dem Namen my_internal_type gibt, beschwert sich der Compiler.
Die Lösung besteht darin, den Namen my_internal_type als abhängigen Namen zu markieren. Dazu gibt es prinzipiell zwei Möglichkeiten:
a) Qualifikation mit der entsprechenden Basisklasse:typename A<T>::my_internal_typeb) Qualifikation mit this (falls es sich um ein Member und nicht um einen Typ handelt)
this->foo();
-
HumeSikkins schrieb:
...In der ersten Phase (vor der Instanziierung) prüft der Compiler die generelle Syntax, sowie alle Namen, die nicht von einem Template-Parameter abhängig ... sind ...
Die Lösung besteht darin, den Namen my_internal_type als abhängigen Namen zu markieren. ...
b) Qualifikation mit this (falls es sich um ein Member und nicht um einen Typ handelt)this->foo();Ist dieser Ausdruck denn "Abhängig vom Template -Parameter" ? Müsste in der Formulierun oben nicht noch "abhängig von der Basisklasse" dazu ? Ich meinte "abhängiger Name" bräuchte das ... oder habe ich das noch falsch verstanden ?
HumeSikkins schrieb:
...
...template <class T> class A<T*> { public: typedef T my_internal_type; };...

Ich hatte mir noch nie Gedanken darüber gemacht, wie man bei Klassentemplates einen Zeiger-Template-Parameter angibt (bei Funktionstemplates ist es ja klar :))
=> Man lernt nie aus !Gruß,
Simon2.
-
Simon2 schrieb:
HumeSikkins schrieb:
...In der ersten Phase (vor der Instanziierung) prüft der Compiler die generelle Syntax, sowie alle Namen, die nicht von einem Template-Parameter abhängig ... sind ...
Die Lösung besteht darin, den Namen my_internal_type als abhängigen Namen zu markieren. ...
b) Qualifikation mit this (falls es sich um ein Member und nicht um einen Typ handelt)this->foo();Ist dieser Ausdruck denn "Abhängig vom Template -Parameter" ?
Jup. Abhängigkeit ist transitiv. foo ist abhängig von this, this ist abhängig von T und damit ist foo abhängig von T. Ein zusätzliches "abhängig von Basisklasse" ist also nicht notwendig.
Anderes Beispiel:
template <class T> struct Base { void foo(T); }; template <class T> struct Derived : public Base<T> { void func() { T x; foo(x); } };Hier braucht man gar keine weitere Qualifizierung: x ist abhängig von T, foo ist abhängig von x, also ist foo abhängig von T und damit ein abhängiger Name.
-
HumeSikkins schrieb:
...
Jup. Abhängigkeit ist transitiv.Das wusste ich wohl, für mich war allerdings die Frage, ob z.B. bei solchem nicht dasselbe gelten müsste:
struct Base { typedef int myType; }; template <class T> struct Derived : public Base { myType f(); };Damit hätten wir zwar eine Abhängigkeit von der Basisklasse, aber keine (noch so indirekte) von T ...
.. aber der gcc sagt mir gerade, dass dem nicht so ist (bzw. dass er damit kein Problem hat). Dann hatte ich das wohl auch mißverstanden.Gruß,
Simon2.
-
Hallo,
ups, da habe ich dich wohl heute zum zweiten Mal mißverstanden
"Normale" Basisklassen sind von der Ausnahme nicht betroffen, da deren Definition zum Zeitpunkt der Definition der abgeleiteten Klasse eindeutig bestimmt ist.
-
Thx
