überprüfen von template variable auf bestimmten typ
-
thordk schrieb:
also ganz naiv würd ich ja einfach mal sagen, dass die idee, einem template für verschiedene typen, verschiedene verhaltensweisen einzuprägen, die ganze template idee ad absurdum führt. schön, dass boost/tr1 da irgendwelche konstrukte für anbieten, aber wenn man mehrere methoden hat, die unterschiedliche eingabetypen erwarten und sich für die jeweiligen typen auch unterschiedlich verhalten, dann sollte man daraus auch unterschiedliche methoden machen.
Mmh, finde ich nicht. Mein Fall:
Ich hab 3 Vectoren geschrieben. Die sind aber keine Container, sondern eben mathematische Vektoren-Klassen. Jetzt macht es natürlich wenig sinn, so einem Vector als Typ beispielsweise string zuzuordnen.
Meine Frage zielt darauf ab, dass ich zur Kompilierzeit solche Fehler vom Anwender gleich als Compilererror anzeigen kann.
Klar, man kann den Vector auch mit einem festen Typ anlegen, aber ich hab ehrlicherweise wenig Lust, das für int, double und float jeweils extra anzulegen.
Man kanns auch dem Verwender der API überlassen, aber naja, ich dachte da kann man halt die Verwendung etwas optimieren und solche "Fehler" gleich aufzeigen.
rya.
-
Die einfachste Lösung ist es, (direkt oder indirekt) das Interfaces der verwendeten Typen einzuschränken - z.B. um zwei Vektoren multiplizieren zu können, muß der verwendete Basistyp einen operator* zur Verfügung stellen.
Der direkte Weg bedeutet, daß du einfach in der Vektor-Mutliplikation auf die Element-Multiplikation zurückgreifst - wenn der verwendete Typ kein hat, beschwert sich der Compiler mit entsprechenden (zugegeben - mitunter etwas kryptischen) Fehlermeldungen. Etwas klarer werden die Fehlermeldungen, wenn du STATIC_ASSERT und ähnliche Hilfsmittel aus der Boost-Bibliothek verwendest (irgendwo habe ich auch eine Hilfsbibliothek gesehen, mit denen du dem Template Typ-Anforderungen mitgeben kannst).
-
Es gibt da z.Bsp. bei den type_traits is_arithmetic
-
hrmpf... nochmal zum Problem zurück, ich zerbrech mir dabei irgendwie den Kopf...
Ich habe eine Basisklasse (die soll nach Möglichkeit NICHT template basiert sein).
In dieser soll eine Konvertierungsfunktion angeboten werden (welche auch gerne generisch sein darf). Allerdings lege ich in der Basisklasse noch nicht fest, dass es überhaupt einen Wert gibt (welcher später mit der Konvertierungsfunktion umgewandelt werden soll). Da leider template Methoden nicht virtual sein dürfen, ist das für mich irgendwie ein Problem, da ich ja, wenn ich einen Pointer wie folgt anlege:Base * temp = new Derived();und anschließend
int c = temp->Convert();aufrufe, die Konvertierungsfunktion der Oberklasse aufgerufen kriege... Das will ich allerdings um jeden Preis vermeiden, da jede neue Klasse auch ihre eigenen Konvertierungsfunktionen haben soll und dann auch diese aufgerufen werden sollen. Mit void * könnte ich zwar zum Ziel kommen, allerdings sind hierbei dann die Hin - und Rückkonvertierungen jeweils auch zwecks Typprüfung relativ aufwendig. Ich hoffe, dass es dafür irgendeine Lösung gibt (die mir allerdings derzeit ein wenig schleierhaft ist
-
Wenn jede der abgeleiteten Klassen ihren eigenen Typ zurückgeben soll, wirst du nur schwer eine gemeinsame Schnittstelle finden.
-
sie soll ja nicht ihren eigenen Typen zurückgeben, eigentlich sollen ALLE long und double unterstützen (also für den Anfang)...
Wenn das ganze jetzt allerdings erweitert werden soll, dass alle nativen Datentypen unterstützt werden sollen, dann hieße das, dass es dabei auch ein ziemliches durcheinander gibt. Ich habe derzeit auch noch das Problem, dass es eigentlich gar keinen Wert so direkt gibt in der Basis-Klasse, der Wert kommt erst in der abgeleiteten Klasse hinzu, von daher kann ich das ganze auch nicht ausimplementieren. Für mich wäre es ja schon genug, wenn ich eine Funktionsdeklaration finden könnte, die allgemein genug ist, dass ich in der abgeleiteten Klasse anschließend mit Templates arbeiten kann.
-
Schon allein das long und double unterstützt werden soll, ist schon problematisch. Und was heißt "ALLE"???
Irgendwie ganz schön strange was du da machen willst. Man könnte höchstens über boost::any nachdenken. Oder wenn es keine Templates sein dürfen, mußt du halt ein void* zurück geben, und derjenige muß casten.
-
das mit dem void * habe ich mir auch schon überlegt, nur das mit dem Cast finde ich halt ein wenig schwierig, vor Allem in Richtung setzen (so mit Typprüfung)...
ALLE bedeutet, dass alle erbenden Klassen die Konvertierungsmöglichkeiten prinzipiell unterstützen sollen.
Jetzt habe ich mir eine Version geschrieben, die zwei Template-Parameter besitzt, in der Basisklasse und in der erbenden Klasse wird einer davon spezialisiert, allerdings erhalte ich die Fehlermeldung "illegal use of explicit template arguments". Gibt es da eine Möglichkeit das zu lösen, mein Code sieht wie folgt aus:
Basisklasse:template <typename T, typename T1> T Convert () { //... }abgeleitet:
template <typename T> T Convert<std::string> () { }Allerdings geht dies nicht, ich hoffe, irgendwer kann mir nun helfen
-
Sorry Vorden, ich hatte vergessen (so wie Braunstein richtig angemerkt hat), daß man nur Klassentemplates partiell spezialisieren kann (falls du dich von meinem Code hast inspirieren lassen?).
Trotzdem verstehe ich den Sinn deiner Konvertierungstemplates immer noch nicht?
Wenn du einfach nur Konvertierungen nach String- und zurück brauchst, dann würde ich über die Stringstreams gehen:template<class T> string toStr(const T& t) { std::ostringstream str; str << t; return str.str(); } template<class T> T fromStr(const string& s, const T& def = T()) { std::istringstream str(s); T t = def; str >> t; return t; }Ansonsten müßtest du für beliebige Datentypen die Funktion 'Convert' mit den jeweiligen Datentypen überladen (jedoch darf sich die Funktion nicht alleine im Rückgabewert unterscheiden, d.h. "int Convert()" und "float Convert()" geht nicht!!!).
Alternativ müßtest du dann jeweils eine Klasse (bzw. Struktur) darum packen:
template<typename R> struct Convert { template <typename T> static R Do(T t) { return R(t); } }; // Aufruf, z.B. int x = Convert<int>::Do(42.123f);Du könntest jetzt dann entsprechend des Rückgabetypes spezialisieren:
template<> struct Convert<int> { template <typename T> static int Do(T t) { std::cout << t; return int(t); } static int Do(std::string s) { return 0; } // Überladung }; // Aufruf z.B. int x = Convert<int>::Do(42.123f); // float->int int y = Convert<int>::Do(std::string("hallo")); // string->intMehr fällt mir aber auch nicht mehr ein und ich hoffe, ich habe dich (in Ansätzen) richtig verstanden, was du vorhast???
-
jo, schon, das mit der Spezialisierung ist soweit richtig, allerdings bleibt das Problem dabei trotzdem bestehen, dass ich mit virtual nicht weiter komme... und das ist genau das, was ich bräuchte. Habe es jetzt wie folgt gelöst:
Ich habe ein allgemeines Template und in der abgeleiteten Klasse die entsprechend spezialisierten Templates. Ich hoffe, dass das den gewünschten Effekt bringt. Das mit der partiellen Spezialisierung habe ich inzwischen auch gefunden, also, dass es lediglich auf Klassenebene möglich ist. Habe mir auch irgendwie ein Interface überlegt, welches entsprechend angebunden wird als "Converter" und dann eben "DoConvert" des entsprechenden Interfaces aufgerufen wird. Das muss ich mir nochmal durch den Kopf gehen lassen, ob die direkte Implementierung oder der Weg über das Interface sinnvoller sind.
Intern soll auch die Konvertierung über die stringstreams ablaufen, nur der Punkt daran ist eben, dass ich die Konvertierung vom Anwender fernhalten wollte und möglichst generisch halten wollte (das ganze bezieht sich auf eine Bibliotheksfunktion, die später von Entwicklern in der Firma benutzt werden soll).
Von daher wollte ich das mit den stringstreams weg kapseln, wenn jetzt eine "bool ReadInt (int * val)" aufruft, soll er halt auch sein int kriegen (dass es sich um ein bool handelt als Rückgabewert und nicht der Wert als Rückgabewert angegeben wird, hat andere Gründe.
-
bool ReadInt (int * val);
Ganz schlechtes Design!
Damit hast du wieder eine "old-school" C Funktion erzeugt (oder mußt du ein C-API entwickeln?)
Besser wären auf jeden Fall Referenzen bzw. Ausnahmen etc.