VC++ workaround: template<class T> class C<T&>{};
-
sorry, ich weiss nicht wie dieses C++ feature heisst.
ich will ein template fuer referenzen spezialisieren:
template<class T> struct isReference { enum { result = 0 }; }; template<class T> struct isReference<T&> { enum { result = 1 }; };
das ist ja standardkonform.
doch der VC++ (7 in diesem fall, aber ich nehme an, dass der 6er es genausowenig kann) meldet nen syntax error -> ergo: dieses feature wird nicht unterstuetzt.doch das kann man sicher irgendwie simulieren - boost und loki koennen das ja auch -> nur ich steig durch den code leider nicht durch
-
sorry, ich weiss nicht wie dieses C++ feature heisst
Partielle Template Spezialisierung.
ich will ein template fuer referenzen spezialisieren
Die gute Nachricht: Wenn du nur rausfinden willst (ja/nein), ob es sich bei T um eine Referenz handelt, dann kann dir geholfen werden. Wenn du allerdings von T& auf T willst, dann gibt es keinen Workaround. Das erfordert zwingend partielle Spezialisierung.
Nun zu den Workarounds.
Der für den VC 7 ist einfach:// Macht aus einem Typ einen anderen Typ // Vorteil: Keinerlei Einschränkungen wie z.B. "T hat Default-Ctor" // nötig. template <typename T> struct Type2Type { Type2Type(){} // VC7 }; // es gilt garantiert: sizeof(yes) != sizeof(no) typedef char (&yes)[1]; typedef char (&no) [2]; template <class T> struct isReference { private: // Kann nur für Referenzen verwendet werden, da Type2Type keinerlei // Konvertierungen zulässt. template<typename U> static yes is_reference(Type2Type<U&>); // Die drei Punkt matchen von allen möglichen Konvertierungen am // schlechtesten. Diese Funktion ist also der Notanker. static no is_reference(...); public: // sizeof-wonderland stößt die Überladungsauflösung an. enum {result = sizeof(is_reference(Type2Type<T>())) == sizeof(yes)}; };
Mit dem VC 6 wird's brutal komplizierter:
typedef char (&yes)[1]; typedef char (&no) [2]; template <class T> struct isReference { private: // Ok. Ganz langsam. // isReferenceHelper1 ist eine Funktion die ein Type2Type<U> als // Parameter erwartet und einen *Funktionszeiger* vom Typ // U& (*)(Type2Type<U>) liefert. // Man beachte den Rückgabewert der neuen Funktion. // Dieser ist eine *Referenz* template <class U> static U&(* isReferenceHelper1(Type2Type<U>) )(Type2Type<U>); static yes isReferenceHelper1(...); // Und weiter: // isReferenceHelper2 ist eine Funktion, die einen *Funktionszeiger* // vom Typ U& (*)(Type2Type<U>). // Also genau so ein Funktionszeiger, wie er von isReferenceHelper1 geliefert // wird. // // *Der Trick* beruht auf SFINAE = Substitution Failure is not an error // D.h. führt die Auflösung von Templateparametern zu einem ungültigen // *Typ*, dann wird kein Compilerfehler hervorgerufen, // sondern einfach die Templatefunktion aus der Menge der möglichen // Funktionen entfernt. // // Falls *T* eine Referenz (T&) ist, dann wird der Template Parameter // U von isReference1 zu T&. Dadurch wird der Typ des Funktionszeigers aber: // T&& (*)(Type2Type<T&>) // Die Funktion liefert also eine Referenz-auf-Referenz und das ist ein // ungültiger *Typ*. // Die Template-Überladung für T = T& fällt also weg. // Es bleibt die Version mit der Ellipse. Diese ist aufrufbar und liefert yes. // Bingo! Warum jetzt aber dann noch isReferenceHelper2? // Kurz: Bug im VC 6. // Eigentlich sollte isRefrenceHelper1 reichen. Der VC 6 wählt aber dummerweise // immer die Template-Funktion. // Durch die zusätzliche Indirektion (ein Mittel, dass in der Informatik // immer hilft), bekommt er es aber dann doch noch hin. template <class U> static no isReferenceHelper2(U&(*)(Type2Type<U>)); static yes isReferenceHelper2(...); public: enum { result = sizeof(isReferenceHelper2(isReferenceHelper1(Type2Type<T>()))) == sizeof(yes) }; };
Der aufmerksame Leser wird feststellen, dass dies genau die Workaoround aus Loki sind. Andere kenne ich aber nicht und vielleicht helfen ja die Kommentare.
Auf einen Blick:
Template-Workarounds basieren sehr häufig auf zwei Techniken. A) Überladung undSFINAE.
Hilft beides nicht, hilft meist "another level of indirection"
-
liegt es an mir oder an diesem workaround dass mir schwindlich ist?
das lustigste daran ist ja: es funktioniert wirklich
danke vielmals.
den Loki code habe ich mir angesehen - aber jetzt ist mir klar, warum ich das ohne deiner erklaerung nicht kapiert habe...nur eine frage noch: was macht Type2Type?
sehe ich das richtig, dass der VC7 workaround nur eine vereinfachte form des VC6 workarounds ist?
wenn ja, dann ist Type2Type klar
-
sehe ich das richtig, dass der VC7 workaround nur eine vereinfachte form des VC6 workarounds ist?
Richtig. Der VC 7 workaround basiert nur auf Überladungsauflösung. Beim VC 6 muss man etwas mehr arbeiten, da er bei dem VC 7 Code mit einem ICE stirbt.
Vielleicht wird das Type2Type klar, wenn man es erstmal weglässt.
Um mit Überladung zu testen ob T eine Referenz ist, brauchst du eine Funktion die eine Referenz erwartet, sowie eine die mit allem anderen umgehen kann.template<typename U> yes is_reference(U&); no is_reference(...);
Um den Test durchzuführen brauchst du jetzt natürlich noch ein Argument. Sprich ein T.
Nur woher nehmen, wenn nicht stehlen.
Der simpelste Versuch geht in die Hose:template <class T> struct isReference { public: enum {result = sizeof(is_reference(T())) == sizeof(yes)}; };
Das geht nicht, wenn T eine Refrenz ist, da T&() kein gültiger Ausdruck ist.
Ok. Aber noch sind wir nicht am Ende. Wenn wir das T-Objekt nicht direkt erzeugen können, dann vielleicht indirekt:template <class T> struct isReference { private: static T MakeT(); public: enum {result = sizeof(is_reference(MakeT())) == sizeof(yes)}; };
Das geht. Alles läuft toll. Bis irgendjemand folgendes versucht:
isReference<int [5]>::result;
Woops. MakeT liefert nun aufeinmal ein int-Array und das ist nicht erlaubt.
Das Type2Type-Typs ist also nur ein Trick um für einen beliebigen Typ T einen gültigen Typ erzeugen zu können, den wir dann zum Testen verwenden können. Wir haben keine Ahnung was T sein kann. Also suchen wir uns etwas über das wir besser einschätzen können.
<edit>Fehler in der zweiten Version von isReference verbessert.</edit>
-
HumeSikkins schrieb:
Das geht nicht, wenn T eine Refrenz ist, da T&() kein gültiger Ausdruck ist.
manchmal sieht man die typen vor lauter templates nichtdanke!
jetzt ist alles klar.