Templatefunktion überladen: extra const-Versionen und char* für std::string nötig?
-
Hi!
Ich hatte folgende Funktionen in einer Klasse:
void SetInt(int val, int offset = -1); void SetShort(short val, int offset = -1); void SetRaw(const unsigned char* val, unsigned int size, int offset = -1); void SetStr(const std::string& val, int offset = -1);Dann dachte ich mir, mache ich doch eine Templatefunktion für int, short usw. und Spezialisierungen für Rohdaten und Strings.
Doch Spezialisierungen erlauben keine Defaultparameter, deshalb habe ich die Templatefunktion überladen:template<typename T> void Set(T value, int offset = -1); void Set(const unsigned char* value, unsigned int size, int offset = -1); void Set(unsigned char* value, unsigned int size, int offset = -1); void Set(const std::string& value, int offset = -1); void Set(std::string& value, int offset = -1); void Set(const char* value, int offset = -1); void Set(char* value, int offset = -1);Wie man schon sieht, habe ich auch lauter const bzw. non-const Versionen schreiben müssen, sonst wäre eventuell die Templatefunktion aufgerufen worden.
Meine Fragen nun:
Ist es irgendwie möglich, dass man nur eine const-Version schreibt, und trotzdem nicht die Templatefunktion aufgerufen wird, obwohl man einen non-const Typ übergibt?
Und könnte man auch irgendwie die char*-Versionen weglassen und dafür sorgen, dass wenn man char* übergibt, automatisch die std::string Funktionen aufgerufen werden?So muss man sehr viele Funktionen definieren, das finde ich unschön, da kann ich gleich wieder SetRaw, SetStr etc. machen

Danke!
-
Diese Konvertierungen sind keine Aufgabe deiner Klasse, daher lagere diese lieber aus:
// speichert einen allgemeinen rohen Datenblock struct RawBlock { const char *first, *last; }; // Convenience-Funktionen für Bekannte Datentypen // Wenn als Konstruktor von RawData definiert, ist die Konvertierung implizit RawBlock raw(int val; RawBlock raw(short val); RawBlock raw(const unsigned char* val, unsigned int size); RawBlock raw(const std::string& val); // Allgemeine Raw-Funktion (macht etwas anderes als raw, daher anderer Name gerechtfertigt) template <typename T> RawBlock generic_raw(T value); // Und dann in der Klasse void Set(RawBlock raw);
-
Hmm, ich hätte die Klasse beschreiben sollen.
Es ist eine Art "Bytestream", intern wird ein vector<unsigned char> gehalten, und man kann mit Set(...) verschiedene Typen pushen (oder an einem offset setzen) und mit Get(...) soll man diese wieder auslesen können.unsigned char raw[] = { 42, -99, 188 }; const unsigned char* craw = raw; string str = "abc"; const char* c = "abc"; char* cc = (char*)c; bs.Set(craw, sizeof(raw)); // Set(const unsigned char* ...) bs.Set(raw, sizeof(raw)); // Set(unsigned char* ...) bs.Set(string(str)); // Set(const std::string& ...) bs.Set(str); // Set(std::string& ...) bs.Set(c); // Set(const char* ...) bs.Set("abc"); // Set(const char* ...) bs.Set(cc); // Set(char* ...)Das Problem ist, dass wenn ich nur die const-Versionen definiert hätte, würde bei Aufrufen mit non-const Typ die Templatefunktion genutzt werden, und das will ich eben verhindern:
// Wäre "void Set(unsigned char* ...)" nicht definiert, würde "Set(T value ...)" aufgerufen werden bs.Set(raw, sizeof(raw)); // Set(unsigned char* ...)Außerdem würde ich mir gerne die char*/const* char Funktionen sparen. Wenn man also "bs.Set("abc")" macht, soll "void Set(const std::string& ...)" genutzt werden.
-
Mit http://en.cppreference.com/w/cpp/types/enable_if kann man das template nur für bestimmte Typen "aktivieren", bzw. "deaktivieren". Je nachdem was besser passt.
Aber so schlecht finde ich die originale Variante auch nicht. SetFloat, SetString, ... passt doch eigentlich?
-
Ahh interessant, gucke mir mal an ob ich was damit basteln kann.
Naja, weiß nicht, ich hab lieber nur ein Set(...) welches je nach übergebenem Typ anders arbeitet.
-
___someone schrieb:
Aber so schlecht finde ich die originale Variante auch nicht. SetFloat, SetString, ... passt doch eigentlich?
Außerdem bräuchte ich dann immer 2 Versionen, signed und unsigned..