drei fragen zum c++ standard
-
ein const_iterator ist kein const iterator, da muss man utnerscheiden. ein const iterator kann tatsächlich nicht verändert werden, während ein const_iterator auf objekte zeigt die nicht verändert werden können.
Was den Ctor angeht: Die Lebensdauer eines Objekts beginnt nach Ablauf seines Ctors, was bedeutet, dass das Objekt auch erst nach der vollständigen Konstruktion const oder non-const sein kann. Demzufolge gibts schon von der Semantik der Ctoren her keinen Unterschied zwischen Ctoren für const und non-const Objekte.
-
äh. huch?
ein "const_iterator" entspricht einem "T const*".
ein "iterator const" entspricht einem "T* const".wie also willst du mit deinem "const ctor" const_iterator überflüssig machen? oder willst du dass man einen "iterator const" weiterschieben kann? das wäre aber sowas von hässlich.
bei der placement delete sache muss ich dir recht geben, wäre nicht doof wenn man den direkt aufrufen könnte. allerdings halte ich es andrerseits auch wieder für ziemlich egal, da ich für meinen teil selten bis nie mit placement new/delete arbeite.
was mich da wesentlich mehr stört ist dass es auf einigen systemen (windows z.b.) so umständlich ist die new implementierung auszutauschen. das redefinieren des globalen operator ist zwar theoretisch möglich, praktisch aber nicht einsetzbar sobald man die DLL runtime verwendet. klar, das ist jetzt nicht die schuld des C++ standard, aber es passt für mich irgendwie zum thema.
-
pumuckl schrieb:
Die Lebensdauer eines Objekts beginnt nach Ablauf seines Ctors, was bedeutet, dass das Objekt auch erst nach der vollständigen Konstruktion const oder non-const sein kann.
über lebensdauer mag man diskutieren können. gehört ein ctor dazu oder nicht? das ist aber auch nicht wichtig. es geht hier nicht um lebensdauer sondern um den typ einer instanz. typ und const-ness werden gleichzeitig untrennbar bestimmt. und beides wird vor dem aufruf des ctors bestimmt. wenn dem nicht so wäre, könnte man ja nie den richtigen ctor aufrufen. dementsprchend kann man deinem argument zustimmen, es ist aber für die sache hier nicht relevant.
@hustbaer
du meinst also, dass ein const iterator nicht das gleiche ist wie ein const_iterator? da bin ich mir nicht sicher. ein const_iterator wird bei const instanzen verwendet. es gibt bei allen klassen verschiedene ebenen an const-ness. darf man die elemente einer konstanten liste ändern oder nicht? oder ist nur die liste als solche konstant, dh die anzahl der element und deren reihenfolge? braucht man einen const iterator in der art, dass man ihn nicht bewegen kann? ist das dann überhaupt sinnvoll? die hauptaufgabe eines iterators ist das iterieren, sodass ein unbeweglicher iterator relativ sinnlos ist. in meinen augen halt. aber wie bei der konstanten liste ist das sicher subjekt. beim iterator würde ich die konstantheit eher auf die element legen und nicht auf das bewegen. will ich einer funktion einen unbeweglichen iterator übergeben, kann ich gleich eine referenz auf das aktuelle element übergeben.das problem der prozessübergreifenden ersetzung von new und delete ist eher kompliziert. beim gcc sind beides funktionen der libgcc (bzw gcc_s), die man überschreiben könnte um dann die exe mit -export-dynamic zu compilieren. damit würden auch alle bibliotheken die new/delete funktionen der exe verwenden. wie das im windows geht, weiß ich nicht. bin kein windowsprogrammierer

-
besserwisser schrieb:
du meinst also, dass ein const iterator nicht das gleiche ist wie ein const_iterator? da bin ich mir nicht sicher. ein const_iterator wird bei const instanzen verwendet. es gibt bei allen klassen verschiedene ebenen an const-ness. darf man die elemente einer konstanten liste ändern oder nicht? oder ist nur die liste als solche konstant, dh die anzahl der element und deren reihenfolge? braucht man einen const iterator in der art, dass man ihn nicht bewegen kann? ist das dann überhaupt sinnvoll? die hauptaufgabe eines iterators ist das iterieren, sodass ein unbeweglicher iterator relativ sinnlos ist. in meinen augen halt. aber wie bei der konstanten liste ist das sicher subjekt. beim iterator würde ich die konstantheit eher auf die element legen und nicht auf das bewegen. will ich einer funktion einen unbeweglichen iterator übergeben, kann ich gleich eine referenz auf das aktuelle element übergeben.
Bei einem Iterator kann man klar zwischen dem zeigenden Etwas (dem Iterator selbst) und dem Objekt, auf das gezeigt wird unterscheiden und natürlich können beide unabhängig const sein oder nicht. Nun ist es selbstverständlich denkbar, dass man const durchreicht - ein konstanter Iterator könnte beim Dereferenzieren auf ein konstantes Objekt zeigen. Man gewinnt dadurch nichts und es widerspricht dem Prinzip der geringsten Überraschung (weil eben Zeiger, als Grundmodell eines Iterators, dieses Verhalten nicht zeigen). Umgekehrt genügt auch unter dieser Bedingung die Konstanz des Iterators nicht, um Konstanz des Objektes, auf das gezeigt wird, zu garantieren. Warum? Weil uns nichts daran hindert, den konstanten Iterator einfach in einen nicht-konstanten Iterator zu kopieren und damit weiterzuarbeiten und die eigentlich konstante Sequenz doch noch zu verändern - so etwas darf nicht sein. Das Verhalten von const_iterator ist etwas ganz Anderes als des Verhalten iterator - und nicht bloß ein Modus seiner Funktionalität.
Zu placement-delete kann ich nur spekulieren. Gezeigt wurde bisher kein Beispiel, in dem der konkrete Nutzen eines solchen Features evident wird. Placement new ist ein ganz anderer Fall: es gibt keine Möglichkeit, diese Funktionalität auf anderem Wege zu erhalten. Operator delete ist bereits eine recht interessante Funktion: wir können bekanntlich diese Funktion in einer Klasse überladen. Wenn nun eine Klasse polymorph ist, muss beim delete (intern vermittelt durch den virtuellen Destruktor) die entsprechende Überladung, die zum dynamischen Typ des ursprünglichen Objekts passt (beim eigentliche Aufruf ist das Objekt schon tot, wir haben es hier also mit einer Art "statisch-virtuellen" Funktion zu tun - ein sehr spezieller Fall), aufgerufen werden. Falls wir nun nun placement-delete-Überladungen zulassen (das ist bereits der Fall) und diese über eine entsprechende delete-Form aufrufen wollen, stoßen wir auf einige Probleme:
1. Die betreffende placement-Form könnte in der Basisklasse fehlen (oder schlimmer: eine weniger gute placement-Form auswählen) - was tun? Auf gut Glück versuchen und im Zweifel mit einem Fehler aussteigen oder lieber gleich ill-formed machen?
2. Die betreffende placement-Form könnte in der abgeleiteten Klasse verdeckt sein - wieder ein seltsamer Fall, dann können wir nur über die Basisklasse löschen.
3. Der Destruktor der Basisklasse weiß nichts von Überladungen in abgeleiteten Klassen - er muss aber irgendwie in der Lage sein, mit diesen Überladungen zu kommunizieren, unabhängig von ihrer Signatur. Das ist prinzipiell machbar, aber kaum ohne Overhead: ein Totschlagargument, schließlich wollen wir nicht für Features bezahlen, die nicht genutzt werden und new/delete-Überladung ist bereits vergleichsweise selten.Es müsste
1. ein hinreichender Nutzen demonstriert werden und
2. die Machbarkeit einer Implementation, die bei Nichtnutzung ohne signifikanten Overhead auskommt, gezeigt werden und
3. die Semantik in komplizierter Fällen (s.o. - das sind mit einiger Sicherheit nicht alle Probleme, die sich ergeben) geklärt werden,
bevor man so etwas als Spracherweiterung in Angriff nehmen könnte.
-
Was sollte denn ein const-Ctor denn genau bewirken?
a) Das Objekt soll während der Konstruktion nicht verändert werden. Das impliziert eigentlich nur, dass der Ctor Rumpf leer ist und alles bereits in der Initialisierungsliste geschieht. Außerdem gibt es das Objekt während der Konstruktion eben noch nicht (-> Lebensdauer), also hieße das, dass die Elemente des Objektes nach ihrer Initialisierung konstant bleiben. Der leere Ctor-Rumpf garantiert das, also ist dafür kein extra const-Ctor nötig.
b) Das Objekt soll nach der Konstruktion konstant bleiben. Ob ein Objekt nach seiner Konstruktion verändert wird oder nicht, hat keinerlei Auswirkungen auf die Art, wie es konstruiert wird. Damit wäre diese Interpretation eines const-Ctors auch widersinnig.
Ich lass mich gern eines besseren belehren, aber ich sehe keinen Nutzen für einen extra-Ctor für const Objekte.Was den const Iterator angeht: natürlich ist ein Objekt, dessen Zweck die Veränderung des eigenen Zustandes ist, das aber als const deklariert wird, von fragwürdigem Nutzen. Dennoch ist es ein legales Konstrukt. Den const_iterator zu verwerfen und statt dessen als const iterator zu bezeichnen ändert die Semantik des keywords const in der Sprache. Ein const bei einem Objekt bedeutet in der Sprache immer, dass das Objekt selbst seinen Zustand nicht ändert. Ein Objekt als const zu deklarieren obwohl es verändert werden kann, nur weil das Objekt auf das es verweist nicht verändert werden kann, würde die Semantik des keywords const für den Einzelfall verändern, was widerum garnicht gut ist.
Was die const list angeht heißt const in erster Linie (Sprachsemantik) nur, dass die Liste selbst nicht verändert werden darf. Ob man das jetzt so interpretiert, dass die Elemente der liste nicht zur Liste selbt gehören (und damit verändert werden dürfen) oder nicht, ist Sache der Auslegung des Konzeptes einer Liste. in der STL gehören die Elemente eines Containers zum Container dazu, deshalb können die Elemente des Containers nicht verändert werden, wenn er const ist. Man kann sich aber durchaus implementationen vorstellen, wo die Liste nur als Liste von Verweisen interpretiert wird und die Elemente auf die verwiesen wird nicht zwangsläufig const sind. Bei so einer Implementation würde wahrscheinlich neben der list auch eine const_list angeboten analog zum const_iterator. Auch hier wäre eine const List (mit veränderbaren Elementen) nicht das selbe wie eine const_list (wo die Liste zwar ergänzt werden könnte, nicht aber ihre Elemente verändert). Dieses Komplizierte Bild ergibt sich hauptsächlich bei Objekten, die auf andere Objekte verweisen, wie eben iteratoren. Bei Zeigern bietet die Sprache zwei const-Möglichkeiten an, nämlich für den Zeiger selbst und für das referenzierte Objekt. Da etwas wie ein "const iterator const" aber nicht in die Sprache passt, muss man das const für die referenzierten Elemente irgendwo mit in den Typen packen:T * iterator std::list<T> //objekt und referee veränderbar T const * const_iterator std::list<const T> //objekt veränderbar, referee const T * const const iterator --- N/A --- //objekt const, referee veränderbar T const * const const const_iterator const std::list<T> //objekt und referee const
-
pumuckl schrieb:
Was sollte denn ein const-Ctor denn genau bewirken?
a) Das Objekt soll während der Konstruktion nicht verändert werden. Das impliziert eigentlich nur, dass der Ctor Rumpf leer ist und alles bereits in der Initialisierungsliste geschieht....Nicht unbedingt !
struct Obj; struct ObjListe { vector<Obj*> objs; }; struct Obj { Obj(ObjListe& ol) { ol.objs.push_back(this); } };Mag man für tolles Design halten oder nicht, aber hier wird der Konstruktor sinnvoll und trotzdem nicht zur Veränderung des Objekts eingesetzt.
Ich finde dabei viel wesentlicher, dass man (soweit mir einfällt) einen Konstruktorcall niemals in einem Zusammenhang auslösen kann, der einer const-Zusage für das betroffene Objekt gerecht wird.
Gruß,
Simon2.
-
@besserwisser:
Ja, ein nicht verschiebbarer Iterator macht wenig Sinn. Allerdings halte ich die Idee die Bedeutung von "const" zu "redefinieren" nicht für gut. Wie camper schon geschrieben hat führt das zu Überraschungen. Stell dir vor du machst eine Liste aus Iteratoren die auf bestimmte Elemente eines Containers zeigen. Nun übergibst du eine const Referenz auf diese Liste an irgendeine Funktion. Diese Funktion könnte dann die Iteratoren in der Liste verschieben - trotz const Referenz. Nicht sehr intuitiv.
beim gcc sind beides funktionen der libgcc (bzw gcc_s), die man überschreiben könnte um dann die exe mit -export-dynamic zu compilieren. damit würden auch alle bibliotheken die new/delete funktionen der exe verwenden. wie das im windows geht, weiß ich nicht. bin kein windowsprogrammierer

Unter Windows geht das leider garnicht so einfach. Es fehlt nämlich das "-export-dynamic" Feature, bzw. sind DLLs unter Windows einfach ziemlich unterschiedlich zu SOs unter Linux. Das Problem ist dass die anderen DLLs sich nicht die Funktion "operator new, egal woher sie kommt" importieren, sondern genau angeben aus welcher DLL sie diesen "operator new" haben möchten. Man kann nun zwar hergehen und in der .exe einen eigenen "operator new" definieren, und diesen auch exportieren, allerdings wird ihn kein Code ausser dem in der .exe verwenden. Die "Lösung" ist die Runtime DLL nach dem Laden im Speicher zu patchen. Relativ "pfui" wenn du mich fragst

-
besserwisser schrieb:
...typ und const-ness werden gleichzeitig untrennbar bestimmt. und beides wird vor dem aufruf des ctors bestimmt...
Das verstehe ich nicht. Nenn mal ein Beispiel ...
Ich kenne "const" nur als Qualifier für einen Bezeichner (niemals als Eigenschaft einer Instanz). Und als solcher liefert er eine "Schnittstellenzusage" für diesen Bezeichner ("Über mich wird die Instanz nicht verändert") .... aber doch nicht über ein Objekt selbst.In die Falle war man bei unserem alten C-Compiler (Standard ?) schon gestolpert ... mit dem Erfolg, dass Folgendes nicht erlaubt war:
f(char const* s); char text[100]; strcpy(text, "Simon2"); f(text); // "invalid type conversion from 'char*' to 'char const*'Was dem Verständnis von const eigentlich total zuwider läuft !
Warum sollte es verboten sein, einer Funktion, die zusagt, die Übergabedaten nicht zu verändern, Daten zu übergeben, die das sogar zuließen ?Gruß,
Simon2.
-
Also ein const-Ctor ist einfach Unsinn.
Hier wird IMMER schreibend auf das (noch gar nicht wirklich existente Objekt) zugegriffen. Zumindest der this-Zeiger wird immer (implizit) angefasst, wenn das Objekt konstruiert wird. Insbesondere auch bei const-Objekten muss ja gerade der Konstruktor dafür sorgen, dass ein solches Objekt überhaupt definierte Werte bekommt. const-Methoden dürfen aber per Definition das Objekt nicht verändern. Das gilt für alle Konstanten. Sie sind zum Zeitpunkt ihrer Definition noch nicht Konstant. Sonst könnten sie gar nicht definiert werden.
-
Ich glaube der OP möchte eine ctor, der nur verwendet werden darf, wenn das damit konstuierte Objekt selbst als "const" markiert ist.
Also wenn man z.B. schreibt:void foo() { const bar b1(123); // <- hier bar b2(123); // <- hier nicht }Das würde es ermöglichen Objekte zu basteln die man nur "von const nach const" kopieren kann, aber nicht "von const auf nicht-const", z.B. so:
class foo { public: foo(foo&); foo(foo const&) const; // <- der "const-ctor" };Das wiederum würde sicherstellen dass "const" sozusagen "erhalten bleibt" (zumindest solange es keiner wegcastet), und würde neue, ungeahnte Möglichkeiten eröffnen die Sprache zu vergewaltigen.
Genau aus dem Grund finde ich auch dass es keine Gute Idee ist

-
hustbaer schrieb:
und würde neue, ungeahnte Möglichkeiten eröffnen die Sprache zu vergewaltigen.
So wird es sein. Natürlich ist ein Iterator, der konstant sein muss kein Iterator, denn iterieren implizit verändern. Oder man interpretiert dieses äußere const als reine Dekoration, dann kann man aber keine richtig konstanten const-Iterator-Objekte haben. So oder so ist das Ganze nicht durchdacht, aber natürlich ist es immer einfacher, anderen das Denken zu überlassen.
-
@hustbaer
genau das war meine idee. jemand, der const wegcastst, ist sowieso selbst schuld. man kann auch gleich inline assembler einbauen und sich den ramm absichtlich kaputt machen. soweit ich das sehe, hätte diese änderung keine auswrikungen auf die bishere semantik von ctors. sie wird nur erweitert. auch die performance leidet darunter nicht.camper schrieb:
So oder so ist das Ganze nicht durchdacht, aber natürlich ist es immer einfacher, anderen das Denken zu überlassen.
also das is echt total fad, wenn du hier solche sachen sagst. ich bin echt für argumente offen, aber das is dann doch etwas unter deinem niveau, meinst du nicht?