list, erase
-
unskilled schrieb:
Ich glaube, wir haben ein wenig aneinander vorbeigeredet - die konvertierung von
iteratorinconst_iteratorist mir klar und die besteht auch jetzt schon... (womit 1.+2. sich erledigt hätten ^^)Nicht wirklich, dieser Satz betrifft nur Punkt 2.
unskilled schrieb:
allerdings hatte ich ein prob, aus dem const_iterator, den erase bekommt nen normalen iterator zu machen - aber das löse ich wohl jz auch, indem ich kein
const nodepointer sondern nen non-const pointer speicher...Das hier dagegen betrifft Punkt 1.

unskilled schrieb:
allerdings wird es dann ja schon wieder doof, die begin() und end() funktionen zu implementieren!?
Ehm, nö.
class TestClass { private: int* m_intPtr; // ... public: int* get_int_ptr() const { // Hier ist m_intPtr vom Typ: int* const // Der Zeiger ist konstant nicht das Objekt, worauf er zeigt. // Das folgende geht also ohne Probleme: return m_intPtr; } }unskilled schrieb:
zu 3. nochmal:
aber mit erase hat man ja dann im endeffekt die umwandlung von einemconst_iteratorin einen (anderen)iterator...Du meinst hier wohl über den Rückgabewert, oder?
Das spielt jedenfalls keine Rolle. Ein Iterator ist nur ein Verweis auf ein Objekt. Wenn du den Iterator aneraseübergibst, ist das Objekt sowieso weg. Was du zurückbekommst, ist ein Iterator auf das nächste Objekt. Dieser Iterator ist natürlich kein konstanter Verweis, denn wenn du eineraseauf einen Container aufrufen kannst, dann hast du einen nicht-konstanten Container. Man darf also die Elemente des Containers modifizieren.Ganz davon abgesehen, da du den nicht-konstanten Container hast, könntest du sogar den Wert, auf welcher dein
const_iteratorverweist, ändern gehen. Einmalstd::distance, und dann einen normaleniteratorholen und einmalstd::advance.Grüssli
-
Aber folgendes geht leider nicht:
struct TestClass { struct base { base *next; base *prev; }; struct node : base { int data; }; struct _m { base a; } m; node* get() const { return static_cast<node*> (&m.a); // return const_cast <node*> ( static_cast<const node*> (&m.a) ); } }; int main() { TestClass tmp; TestClass::node* x = tmp.get(); }erzeugt
MSVC schrieb:
error C2440: 'static_cast' : cannot convert from 'const TestClass::base *' to 'TestClass::node *'
das meinte ich mit const_cast - oder mach ich da was falsch?!
es liegt offensichtlich daran, dass ich die member an sich in dem struct gekapselt habe... brauch ich aber, weil ich 2 dummy-knoten habe und die nach jedem ctor aufeinander zeigen sollen - also hab ich _m einfach nen standard-ctor gegeben und ruf den in jedem ctor auf (was zwar eigtl gar nicht nötig ist, aber ich habs trotzdem mal gemacht
)...bb
-
Du könntest den Dummyknoten dynamisch erzeugen oder verpass dem Dummyknoten Objekt das Keyword
mutable.Grüssli
-
Dravere schrieb:
Du könntest den Dummyknoten dynamisch erzeugen...
und somit wäre der standard-ctor nich mehr exception-safe...
außerdem find ich es unnötig...Dravere schrieb:
oder verpass dem Dummyknoten Objekt das Keyword
mutable.hab ich auch schon dran gedacht, erschien mir aber irgendwie so unelegant...
du würdest wohlmutablenutzen?!bb
edit: oder würdest du den const_cast lassen? eigtl taucht der im kompilierten programm ja so und so nicht mehr auf, oder? außerdem sollte da ja auch nichts schief gehen können, oder? ^^
-
Ob ich den Dummyknoten dynamisch oder per
mutableanlegen würde, ist schwer zu sagen. Ich tendiere allerdings zu dynamisch.Es gibt von mir aus gesehen ein Killerargument gegen die
mutableLösung:int main() { yourlib::list<MassiveObject> list; return 0; }Wenn
MassiveObjectwirklich ein zu grosses Objekt ist, dann hast du hier plötzlich einen Stackoverflow. Es ist zwar ein eher theoretisches Problem, aber ich sehe zu wenig Vorteile bei dermutableLösung, welche mich dazu bringen würden, das Risiko dieses theoretischen Problems einzugehen.Zur Exceptionsicherheit:
Wieso sollte der Konstruktor nicht mehr exceptionsicher sein? Wenn man die Sache richtig umsetzt, dann ist das doch ohne Probleme möglich. Wo siehst du hier ein Problem?Grüssli
-
Dravere schrieb:
[...] Stackoverflow [...]
Die Dummy-Knoten beinhalten aber keine Daten - nur einen Zeiger auf next und prev... so könnte es auch keinen Stackoverflow geben, oder seh ich das falsch?
Dravere schrieb:
Wieso sollte der Konstruktor nicht mehr exceptionsicher sein?
weil ich dann 2
news hätte?!bis jetzt sieht der Standard-CTor so aus:
_m() : anchor_begin(&anchor_end, nullptr), anchor_end(nullptr, &anchor_begin) {}danach würde er dann nicht nur nicht mehr so schön gehen sondern zusätzlich dazu könnte an 2 Stellen eine exception fliegen...
_m() : anchor_begin(nullptr), anchor_end(new base_node) { anchor_begin = new base_node(anchor_end, nullptr); anchor_end->prev = anchor_begin; anchor_end->next = nullptr; }die nullptr sind zwar nicht notwendig und ich könnte einfach den random-wert drin stehen lassen, aber darin sehe ich keinen vorteil...
und der ctor ist so noch nicht mal exception-safe...wenn ich so drüber nachdenke, denke ich fast, dass ich die lösung mit dem const_cast lasse - da er (imho) genau 0takte kostet und niemals konstante daten geändert werden. Somit sollte es auch nicht undefiniertes verhalten liefern können - selbst, wenn der compiler konstante daten in irgend nen read-only speicher schreiben sollte... Richtig?
bb
-
unskilled schrieb:
Die Dummy-Knoten beinhalten aber keine Daten - nur einen Zeiger auf next und prev... so könnte es auch keinen Stackoverflow geben, oder seh ich das falsch?
Ah, sorry, da habe ich nicht genug weit mitgedacht. Habe selber noch nie eine Liste mit Dummyknoten implementiert

Die paar zusätzlichen ifs, welche dann nötig sind, waren mir bisher immer egal.
Dann sieht aber
mutabledurchaus lecker aus
unskilled schrieb:
weil ich dann 2
news hätte?!Ja und? Man könnte zum Beispiel einen
scoped_ptrnehmen, wäre sowieso nicht so verkehrt. Nur weil man zwei news hat, heisst das doch nicht, dass man den Konstruktor nicht Exception sicher machen kann.unskilled schrieb:
bis jetzt sieht der Standard-CTor so aus:
_m() : anchor_begin(&anchor_end, nullptr), anchor_end(nullptr, &anchor_begin) {}Geht sowas überhaupt laut Standard? Da bin ich mir jetzt gar nicht so sicher. Es sollte doch eine Reihenfolge zu beachten sein. Zumindest dürfte es hier eine Warnung geben, ähnlich wie wenn man
thisin der Intialisierungsliste verwendet.unskilled schrieb:
wenn ich so drüber nachdenke, denke ich fast, dass ich die lösung mit dem const_cast lasse - da er (imho) genau 0takte kostet und niemals konstante daten geändert werden. Somit sollte es auch nicht undefiniertes verhalten liefern können - selbst, wenn der compiler konstante daten in irgend nen read-only speicher schreiben sollte... Richtig?
Sofern du dir da ganz sicher bist, dass niemand anderes oder auch du "ausversehen" das Objekt doch verändert, weil du oder der andere sich nicht daran erinnert, dass der Zeiger auf ein nicht konstantes Objekt eigentlich ein Zeiger auf ein konstantes Objekt ist.
Ich mag
const_castnicht, da würde ich ehermutablenehmen, da es das Design sicherer macht.Grüssli
-
Dravere schrieb:
unskilled schrieb:
bis jetzt sieht der Standard-CTor so aus:
_m() : anchor_begin(&anchor_end, nullptr), anchor_end(nullptr, &anchor_begin) {}Geht sowas überhaupt laut Standard? Da bin ich mir jetzt gar nicht so sicher. Es sollte doch eine Reihenfolge zu beachten sein. Zumindest dürfte es hier eine Warnung geben, ähnlich wie wenn man
thisin der Intialisierungsliste verwendet.Nö - die Adresse steht ja schon fest - und was anderes verwende ich ja nicht... kommt auch keine Warnung...
Dravere schrieb:
unskilled schrieb:
wenn ich so drüber nachdenke, denke ich fast, dass ich die lösung mit dem const_cast lasse - da er (imho) genau 0takte kostet und niemals konstante daten geändert werden. Somit sollte es auch nicht undefiniertes verhalten liefern können - selbst, wenn der compiler konstante daten in irgend nen read-only speicher schreiben sollte... Richtig?
Sofern du dir da ganz sicher bist, dass niemand anderes oder auch du "ausversehen" das Objekt doch verändert, weil du oder der andere sich nicht daran erinnert, dass der Zeiger auf ein nicht konstantes Objekt eigentlich ein Zeiger auf ein konstantes Objekt ist.
Ja, ich bin mir sicher, dass niemand was verändert... wenn das Objekt an sich const ist, dann wird bei begin() und end() ein const_iterator erzeugt und man kann nur über advance oder so nen iterator draus machen - aber das ist ja immer so ^^ ansonsten hat man keine möglichkeiten, das objekt zu ändern...
Dravere schrieb:
Ich mag
const_castnicht, da würde ich ehermutablenehmen, da es das Design sicherer macht.Hmm... mutable sieht immer so hässlich aus : D
Da nehm ich lieber irgendwo nen const_cast, den so und so niemand sieht
bb
Danke für deine Hilfe und Geduld

-
unskilled schrieb:
Nö - die Adresse steht ja schon fest - und was anderes verwende ich ja nicht... kommt auch keine Warnung...
Problem wäre aber sowas:
class Inner { int x; public: Inner(Inner* other) : x(0) { other->x = 4; } } class Outer { Inner a, b; public: Outer() : a(&b) // Hier würde dem x in b 4 zugewiesen werden, // Obwohl das Objekt gar noch nicht konstruiert ist. , b(&a) { } }Klar, bei dir ist das nicht der Fall, weil du nur den Zeiger abspeicherst, aber deswegen dachte ich, dass zumindest eine Warnung kommen würde, denn der Kompiler wird sicher nicht genau nachprüfen, was mit dem Zeiger passiert. Er könnte es zum Teil sogar gar nicht.
unskilled schrieb:
Hmm... mutable sieht immer so hässlich aus : D
Da nehm ich lieber irgendwo nen const_cast, den so und so niemand sieht
Naja, ist deine Entscheidung ...
Grüssli
-
jopp - aber auch bei deinem bsp bekomm ich mit dem msvc auf w4 keine warning...
warnt gcc bei sowas?bb