untereinander befreundete Templates ;)
-
Hallo!
Wie kann ich es erreichen, dass alle Spezialisierungen eines Templates untereinander befreundet sind, also z.B.template <typename T> class foo { //wie lautet die friend-Deklaration? private: int _i; T _t; public: template <typename T2> void bar(const foo<T2> &obj) { _i = obj._i; //Ja ja, schlechtes Beispiel, ich weiß... } //... };
-
Im prinzip genauso wie sie mit normalen Klassen ausschaut.
Du musst für jeden möglichen Typ die friend deklaration aufnehmen.Die Frage wäre aber eher, für was brauchst Du das ? Erscheint mir so als gäbe es eventuell eine bessere Lösung
-
Kann man das nicht irgendwie automatisieren und in das Template reinschreiben?
Ich brauch's für eine Cast-Funktion (Polymorphie) meines Smart-Pointer-Templates... Ich muss in dieser Funktion einen Smart-Pointer auf einen anderen Datentyp erzeugen, das geschieht mit einem privaten Konstruktor, dem ein Referenzzähler und der "echte" Zeiger übergeben werden. Es macht in meinem Fall keinen Sinn, diesen Konstruktor für andere Dinge öffentlich zu machen, deshalb möchte ich das über friends lösen.
-
//wie lautet die friend-Deklaration?
template <class U> friend class foo;
Das erklärt alle Instanziierungen foo<U> zum Friend von foo<T>.
Diese "template friend"-Syntax ist zwar Standard, beherrscht aber nicht jeder Compiler.
-
Hm... ?!?
Kannst Du das ein wenig näher erklären ? Das leuchtet mir nicht ein.
Der Compiler muß doch irgendwo anfangen.
foo<int>
foo<float>
foo<string>Wenn er bei foo<int> is weiß er noch ix von foo<float> ?!?
-
Der Compiler muß doch irgendwo anfangen.
Nein. Das heißt ja nicht, dass der Compiler *explizit* eine Friend-Deklaration für alle Typen erstellt. Das ginge in der Tat nicht, da es theoretisch unendlich viele Typen gibt, selbst wenn man nur die Basistypen + Funktionstypen berücksichtigen würde.
Vielmehr ist die friend-Deklaration implizit.
Grundsätzlich gilt: Jede Methode von foo<T> kann auf die private-Bereiche eines anderen foo<T>-Objekts zugreifen. Dabei spielt es keine Rolle, was genau T ist. Der Compiler geht also nicht her und sagt sich: Ok. foo<int> darf auf foo<int> zugreifen, foo<double> darf auf foo<double> zugreifen, foo<long> auf foo<long>...
Er braucht sich ja nur eine Regel a là: "Wenn ich zwei foo-Objekte habe und beide substituieren T mit dem selben Typ, dann kann jedes der beiden Objekte in den Methoden von foo auf die privaten Bereiche des anderen Zugreifen", merken.
Hat man im konkreten Fall nun zwei Objekte a und b mit a vom Typ foo<Bla> und b vom Typ foo<Bla> und versucht a in einer Methode von foo auf ein private-Element von b zuzugreifen, dann muss der Compiler nur noch die Substitution von T prüfen. a substituiert T mit Bla. b ebenfalls. Bla ist gleich Bla, also ist der Zugriff nach der oberen Regel erlaubt.
Nehmen wir jetzt eine einfache friend-Deklaration hinzu:
template <class T> class foo { friend class foo<double>; };
"Erlaube innerhalb der Methoden von foo<T> für ein beliebiges T den Zugriff auf geschützte/private Elemente eines Objekts vom Typ foo<double>".
Es gibt wiederum nur eine Regel, nicht eine für jeden beliebigen Typen T (das können schließlich beliebig viele sein).
In dem Moment wo eine konkrete Zugriffs-Situation eintritt sind die konkreten Typen für die Templateparameter bekannt. Die Parameter aus den Regeln können also substituiert werden und das Ergebnis kann mit der konkreten Situation verglichen werden:
foo<float> f; foo<double> d; f.accessPrivateParts(d);
Das T ist aus der Regel von oben ist hier jetzt float.
"Erlaube innerhalb der Methoden von foo<flaot> den Zugriff auf geschützte/private Elemente eines Objekts vom Typ foo<double>".
d ist vom Typ foo<double>, also kann f in der Methode accessPrivateParts die privaten/geschützen Elemente von d ansprechen.Jetzt kommt die komplizierte friend-Deklaration dazu:
template <class T> class foo { template <class U> friend class foo; };
Diese besagt etwas nach dem Motto: "Erlaube innerhalb der Methoden von foo<T>
den Zugriff auf alle Bereiche eines Objekts vom Typ foo<U>".
Auch diese Regel ist wieder nicht explizit. Sie sagt also nicht:
"Für jeden beliebigen Typ u aus der Menge aller Typen, gehe hin und erkläre foo<u> explizit zum friend von foo<T>"Auch hier ist es erstmal egal mit was für konkretn Typen T und U substituiert werden.
Der Compiler braucht sich nur merken, dass es hier zwei Variablen (T, U) gibt, die beliebig substituiert werden können.
Und egal wie sie substituiert werden, nachher ist der eine Ergebnistyp Freund des anderen.Erst in einer konkreten Situation (da sind alle Typen bekannt) muss der Compiler alle Regeln prüfen.
foo<Bla> bla; foo<Blub> blub; bla.accessPrivateParts(blub);
Hier will ein Objekt bla vom Typ foo<Bla> in einer Methode von foo<Bla> die privaten Elemente von einem Objekt blub vom Typ foo<Blub> ansprechen.
Checken wir die Regeln:
1. Die erste kann nicht passen, da T nur mit einem Typ substituiert werden kann.
Wir haben aber zwei (Bla und Blub). T ist also entweder Bla oder Blub, aber nicht beides gleichzeitig.2. Die zweite kann nicht passen. Wir substituieren T mit Bla fein, aber dummerweise ist blub vom Typ foo<Blub> und nicht vom Typ foo<double>.
3. Die dritte Regel passt: Wir haben zwei konkrete Typen Bla und Blub und zwei variable Parameter T und U. Der Compiler kann jetzt T mit Bla und U mit Blub substituieren.
Dann wird aus der Regel: "Erlaube innerhalb der Methoden von foo<Bla>
den Zugriff auf alle Bereiche eines Objekts vom Typ foo<Blub>".Fertig.
-
Hm, wird Zeit das ich mein VB kram fertig bekomm und wieder was mit C++ mache.
Danke Dir, wie immer sehr gut Erklärt
-
@HumeSikkins: Danke für diese sehr ausführliche und gute Erklärung!
PS: Der gcc 3.2.3 schluckt das problemlos...