vector wrapper für eigenen Container - So Ok?
-
Hallo,
ich wollte vor kurzem einen eigenen Container erstellen, der seine Werte intern in einem
std::vector<T>speichert und managed. Das hat auch sehr gut funktioniert, bis ich irgendwann mal einencontainer<bool>erstellen wollte. Nach einiger Recherche was da schief läuft bin ich darauf gestoßen dassvector<bool>eine Spezialisierung vonstd::vectorist, und keine Rückgabe von Referenzen erlaubt (da die Werte hier als einzelne Bits gespeichert werden und solche ja nicht addressierbar sind).Da ich für meinen Container aber nach außen hin ein konsistentes Interface bereitstellen will, habe ich einen Wrapper für
std::vectorgeschrieben:template <class T> struct vector_wrapper { vector_wrapper(size_t size_) { values.resize(size_); }; void set(size_t index_, T const &value_) { values[index_] = value_; }; T &get(size_t index_) { return values[index_]; }; T const &get(size_t index_) const { return values[index_]; }; std::vector<T> values; }; template <> struct vector_wrapper<bool> { vector_wrapper(size_t size_) { values.resize(size_); }; void set(size_t index_, bool value_) { values[index_] = !!value_; }; bool &get(size_t index_) { return reinterpret_cast<bool &>(values[index_]); }; bool const &get(size_t index_) const { return reinterpret_cast<bool const &>(values[index_]); }; std::vector<char> values; }; template <class T> class container { public: container(size_t size_) : values(size_) {}; void set(size_t index_, T const & value_) { values.set(index_, value_); }; T &get(size_t index_) { return values.get(index_); }; T const &get(size_t index_) const { return values.get(index_); }; private: vector_wrapper<T> values; };Dazu hab ich jetzt ein paar Fragen: Ist das Ganze so Ok oder gibt es einen noch besseren Weg? Casts sagt man ja allgemein nach dass sie böse sind, in diesem Fall auch? Ich sehe nämlich keine möglichkeit das ohne Cast hier zu lösen. Und, gibt es Fälle in denen dieser Ansatz failen könnte?
-
happystudent schrieb:
Ist das Ganze so Ok
Nein, reinterpret_cast ist böse und lässt sich hier vermeiden.
struct bool_wrapper { bool value; operator bool&() { return value; } operator bool const&() const { return value; } }; template <class T> struct vector_wrapper { vector_wrapper(size_t size_) { values.resize(size_); }; void set(size_t index_, T const &value_) { values[index_] = value_; }; T &get(size_t index_) { return values[index_]; }; T const &get(size_t index_) const { return values[index_]; }; std::vector<std::conditiona<std::is_same<T,bool>::value,bool_wrapper,T> values; };
-
Hm, also nach ein paar kleinen Korrekturen funktioniert das schonmal für alle nicht-bool Typen:
struct bool_wrapper { bool value; operator bool&() { return value; } operator bool const&() const { return value; } }; template <class T> struct vector_wrapper { vector_wrapper(size_t size_) { values.resize(size_); }; void set(size_t index_, T const &value_) { values[index_] = value_; }; T &get(size_t index_) { return values[index_]; }; T const &get(size_t index_) const { return values[index_]; }; typedef typename std::conditional<std::is_same<T, bool>::value, bool_wrapper, T>::type _T; std::vector<_T> values; };Beim Versuch einen vector_wrapper vom Typ bool zu instanziieren erhalte ich aber folgende Fehlermeldung:
error C2679: Binärer Operator '=': Es konnte kein Operator gefunden werden, der einen rechtsseitigen Operanden vom Typ 'const bool' akzeptiert (oder keine geeignete Konvertierung möglich)
Dabei wird auf Zeile 11 (die set-Methode verwiesen).
-
Dann füge noch das bei bool_wrapper ein:
void operator=(bool new_value) { value = new_value; }Verstehst du, was meine Idee ist?
-
ungetestet schrieb:
Dann füge noch das bei bool_wrapper ein:
void operator=(bool new_value) { value = new_value; }Verstehst du, was meine Idee ist?
Ok, alles klar. Ja, ich versteh schon was deine Idee ist, hab das Ganze jetzt so realisiert (ein entsprechender Constructor mit Default-Argument hats auch getan):
template <class T> class container { public: container(size_t size_) { values.resize(size_); }; void set(size_t index_, T const &value_) { values[index_] = value_; }; T &get(size_t index_) { return values[index_]; }; T const &get(size_t index_) const { return values[index_]; }; private: struct bool_wrapper { bool_wrapper(bool value_ = false) : value(value_) {}; bool value; operator bool&() { return value; } operator bool const&() const { return value; } }; typedef typename std::conditional<std::is_same<T, bool>::value, bool_wrapper, T>::type T_; std::vector<T_> values; };Funktioniert wie ne Eins und ist auch noch kürzer als meine Lösung

Dann hab ich endlich diese nervige Spezialisierung umgangen, ich frag mich echt was sich die Typen dabei gedacht haben als sie die vector Klasse gemacht haben

-
container(size_t size_) { values.resize(size_); };Da sollte
explicithin.void set(size_t index_, T const &value_) { values[index_] = value_; }; T &get(size_t index_) { return values[index_]; };Was soll das
set()bringen? Wennget()eh schon eine Referenz zurückgibt und dir so Schreibzugriff gewährt, kannst du dir die Kapselung gleich sparen. Ganz abgesehen davon, dassoperator[]eine intuitivere Syntax ermöglicht.happystudent schrieb:
ich frag mich echt was sich die Typen dabei gedacht haben als sie die vector Klasse gemacht haben

Sie wollten
vector<bool>als Bitset behandeln und platzspeichernd arbeiten. Dass das nicht immer ideal ist, wurde erst mit der Zeit klar.
-
Nexus schrieb:
Was soll das
set()bringen? Wennget()eh schon eine Referenz zurückgibt und dir so Schreibzugriff gewährt, kannst du dir die Kapselung gleich sparen. Ganz abgesehen davon, dassoperator[]eine intuitivere Syntax ermöglicht.Jo, das stimmt, das
set()ist ziemlich redundant, werd ich wohl durchoperator[]ersetzen.Nexus schrieb:
Sie wollten
vector<bool>als Bitset behandeln und platzspeichernd arbeiten. Dass das nicht immer ideal ist, wurde erst mit der Zeit klar.Warum wird sowas dann eigentlich nicht gefixt?
Also Spezialisierungen sind ja schön und gut aber ich finde die Funktionaliät nach außen hin sollte sich dadurch nicht verändern... Sonst kann man ja einfach ne neue Klasse machen, die auf Speicher optimiert ist (ka, zum Beispiel
bool_vectoroder so), würde auf jeden Fall viel Konfusion verhindern
-
happystudent schrieb:
Jo, das stimmt, das
set()ist ziemlich redundant, werd ich wohl durchoperator[]ersetzen.get()kannst du auch gleich entfernen.happystudent schrieb:
Warum wird sowas dann eigentlich nicht gefixt?
Weil es bestehenden Code kaputt machen würde.
-
Nexus schrieb:
happystudent schrieb:
Warum wird sowas dann eigentlich nicht gefixt?
Weil es bestehenden Code kaputt machen würde.
Gibt es wirklich irgendjemanden, der sich auf das kaputte Verhalten verlassen hat? Oder hast du da ein gutes Beispiel?
-
...
-
Gibt es wirklich irgendjemanden, der sich auf das kaputte Verhalten verlassen hat?
Es ist nicht kaputt, es ist einfach anders.Manchmal bekommt man auch einen gehörigen Performance-Boost. Und das, ohneboost::dynamic_bitset.