Unterschied zwischen leerem Destruktor und default Desktruktor
-
Ich habe es jetzt 2x ausführlich erklärt. Den Fakt, dass idie Optimierung nicht gemacht wird, obwohl sie das beobachtbare Verhalten nicht ändern würde, könnt ihr mit jedem Compiler nachvollziehen, egal ob ihr meine Erklärung nun versteht oder nicht. Was soll ich da noch mehr tun? Mehr Beispiele liefern, wieso es nicht so einfach ist? Ihr akzeptiert nicht einmal das denkbar einfachste Beispiel, das ich nicht einmal selbst schreiben musste, sondern einfach unverändert das Simpelbeispiel des TE genommen habe. Da ist doch ausgeschlossen, dass euch obskurere Beispiele helfen.
Ihr habt also:
- Objektiv prüfbare Fakten, dass die Vorgeschlagene Optimierung nicht gemacht wird
- Eine Erklärung dazu, wieso es technisch schwierig bis unmöglich ist, die vorgeschlagene Optimierung durchzuführen
- Gegenbeispiele zu eurer Gegenerklärung, wieso ihr denkt, dass das alles ganz einfach wäre
Und ihr sagt trotzdem "Nein, das glaube ich nicht". Was soll ich da noch weiter machen? Nein, beantwortet diese Frage nicht, ich sehe nämlich keinen Sinn, noch mehr zu liefern. Es ist alles gesagt, jede weitere Antwort wäre eine Wiederholung des bereits gesagtem mit anderen Worten. Das könnt ihr oben selber nachlesen.
-
@seppj Hä? Dein Beispiel oben ist unzulässig, weil es undefiniert ist. Da ist es völlig wurscht, welchen Effekt unsere Umschreibung hat.
Und Du hast dich wieder nicht auf meine Antwort bezogen. Zitiere mal einen Teil davon, und erkläre mir, wo ich etwas missverstanden habe. Und deine Erklärung ist sowas von absoluter bs:
Jetzt willst du ernsthaft einen Optimierer bauen, der for-Schleifen semantisch(!) analysiert und erkennt, dass die for-Schleife aus dem Beispiel durch ein memmove ersetzbar ist?
Willst du mich jetzt völlig veralbern oder was? Das ist schon längst gängige Praxis!
-
Also aus meiner Sicht ist es Wurst ob es schon gemacht wird oder nicht. Für mich ist "es wird nicht gemacht" und daraus folgt es ist sehr schwer oder auch unmöglich keine logische Begründung. Mit glauben hat das ja nun nichts zu tun. Würde mich daher freuen, wenn auf meine Anmerkung genauer eingegangen wird.
-
@seppj sagte in Unterschied zwischen leerem Destruktor und default Desktruktor:
Jetzt ersetzen wir einfach den Destruktor durch den default.
#include <iostream> struct Point { int x,y; ~Point()=default; }; int main() { Point p; std::cout << p.x << ' ' << p.y << '\n'; // Hoppla, alles 0? }
Ups.
Muss da nicht
Point p = Point();
oder sowas stehen damit man hier zero-init bekommt? War zumindest mal so...
-
@SeppJ: Ich würde auch gerne deine Definition von "benötigt Codeinspektion" wissen.
-
@hustbaer Das ist der springende Punkt. Es ist undefiniert, uninitialisierte
int
Variablen auszuwerten. Aber sobald der Code sie initialisiert, passiert das unabhängig davon, ob ein Destruktor definiert wurde oder nicht. Also macht das Beispiel einfach allgemein keinen Sinn.
-
Ich habe halt mit dem Beispiel des TE gearbeitet, um zu zeigen, dass selbst am denkbar einfachsten Beispiel eine einfache Pauschalersetzung zu einer Verhaltensänderung führt. Und schönerweise sogar auf eine Art und Weise, die Parallelen zu einem der größten Fehler der Softwaregeschichte aufweisen.
Hier, der nächste Verhaltensunterschied bei automatischer Pauschalersetzung, am wohl zweiteinfachsten denkbaren Beispiel:
class Foo { ~Foo(){} }; struct Bar: public Foo { ~Bar(){} };
class Foo { ~Foo()=default; }; struct Bar: public Foo { ~Bar()=default; };
Man muss halt schon wissen, was man da tut und den Code und sein Verhalten verstehen.
-
@c-olumbo Weisst du zufällig die Stelle wo definiert wird wann Member einer Klasse T, wenn sie mit T() instanziert wird (also z.B.
new T()
statt einfach nurnew T
) value-initialized werden und wann nicht?Ich weiss nur mehr: sobald die Klasse einen nicht-trivialen Ctor hat wird nix mehr von selbst value-initialized. Was schnell dadurch passieren kann dass man ein Member hinzufügt welches nen nicht trivialen Ctor hat. Die restlichen Bedingungen weiss ich aber nimmer... Ist der Dtor da wirklich egal?
-
@seppj Ich muss gestehen ich bin wohl zu doof für das Beispiel. Ich meine klar, im ersten Fall haben Foo und Bar beide einen nicht-trivialen Dtor und im zweiten Fall beide einen trivialen. Was offensichtlich ist, und daher denke ich mal dass es dir nicht darum geht...
-
@hustbaer sagte in Unterschied zwischen leerem Destruktor und default Desktruktor:
@seppj Ich muss gestehen ich bin wohl zu doof für das Beispiel. Ich meine klar, im ersten Fall haben Foo und Bar beide einen nicht-trivialen Dtor und im zweiten Fall beide einen trivialen. Was offensichtlich ist, und daher denke ich mal dass es dir nicht darum geht...
Das eine compiliert, das andere nicht.
-
Ja die Version mit "= default" kompiliert wobei das komisch ist.
In Foo sollte der Destructor eigentlich private sein. Scheinbar wird aber beim verwenden von "=default" der destructor public definiert.
Es könnte natürlich auch sein, dass in dem konkreten beispiel vom Compiler überhaupt kein destructor definiert wird, da die objekte keine member enhalten wofür ein destruktor notwendig wäre (soweit ich das verstehe)
-
@seppj Achje, ja, wegen dem privaten Dtor. Danke. Ist aber auch feig struct und class so zu mischen
-
@hustbaer Wenn der Initializer
()
ist, wird sie value-initialized.To value-initialize an object of type
T
means:
(8.1) ifT
is a (possibly cv-qualified) class type with either no default constructor ([class.ctor]) or a default constructor that is user-provided or deleted, then the object is default-initialized;
(8.2) ifT
is a (possibly cv-qualified) class type without a user-provided or deleted default constructor, then the object is zero-initialized and the semantic constraints for default-initialization are checked, and ifT
has a non-trivial default constructor, the object is default-initialized;
(8.3) ifT
is an array type, […]
(8.4) otherwise, the object is zero-initialized.Natürlich ist der Destruktor egal: Es geht hier um Initialisierung, und da zählen die Konstruktoren. Ein Aggregat ist ebenfalls völlig unabhängig von etwaigen Destruktoren definiert.
@hustbaer sagte in Unterschied zwischen leerem Destruktor und default Desktruktor:
Ich weiss nur mehr: sobald die Klasse einen nicht-trivialen Ctor hat wird nix mehr von selbst value-initialized. Was schnell dadurch passieren kann dass man ein Member hinzufügt welches nen nicht trivialen Ctor hat.
Oben können wir sehen, dass das nicht stimmt. Siehe auch hier. Wobei anzumerken wäre, dass bei GCC der erste
assert
fliegt. Könnte sich um einen Bug handeln?
-
Oh, ein private Destruktor verhält sich anders als ein public, wer hätte das gedacht.
Ich weiss echt nicht worauf du hinaus willst. Was hat das alles mit der (Un)möglichkeit zu tun eine gewisse Optimierung automatisch zu machen, die sonst nur durch eine simple Änderung des Code ermoeglicht wird.
-
@hustbaer sagte in Unterschied zwischen leerem Destruktor und default Desktruktor:
@seppj Ich muss gestehen ich bin wohl zu doof für das Beispiel. Ich meine klar, im ersten Fall haben Foo und Bar beide einen nicht-trivialen Dtor und im zweiten Fall beide einen trivialen. Was offensichtlich ist, und daher denke ich mal dass es dir nicht darum geht...
Ist ganz einfach: ~Foo()=default; erzeugt nen public inline Destruktor... Mit trivial oder nicht hat das überhaupt nichts zu tun.
-
@tggc sagte in Unterschied zwischen leerem Destruktor und default Desktruktor:
Oh, ein private Destruktor verhält sich anders als ein public, wer hätte das gedacht.
Du hast das Beispiel nicht verstanden. Guck noch einmal genau hin.
-
@firefly sagte in Unterschied zwischen leerem Destruktor und default Desktruktor:
Ja die Version mit "= default" kompiliert wobei das komisch ist.
In Foo sollte der Destructor eigentlich private sein. Scheinbar wird aber beim verwenden von "=default" der destructor public definiert.
Es könnte natürlich auch sein, dass in dem konkreten beispiel vom Compiler überhaupt kein destructor definiert wird, da die objekte keine member enhalten wofür ein destruktor notwendig wäre (soweit ich das verstehe)Nein, da ist kein Trick dabei, dass der Destruktor durch das default plötzlich public wird. Der Destruktor ist nach wie vor private. Versuch mal ein Objekt von der Klasse zu erstellen und den Destruktor aufzurufen. Es wird an der Sichtbarkeit scheitern.
-
@tggc sagte in Unterschied zwischen leerem Destruktor und default Desktruktor:
@hustbaer sagte in Unterschied zwischen leerem Destruktor und default Desktruktor:
@seppj Ich muss gestehen ich bin wohl zu doof für das Beispiel. Ich meine klar, im ersten Fall haben Foo und Bar beide einen nicht-trivialen Dtor und im zweiten Fall beide einen trivialen. Was offensichtlich ist, und daher denke ich mal dass es dir nicht darum geht...
Ist ganz einfach: ~Foo()=default; erzeugt nen public inline Destruktor... Mit trivial oder nicht hat das überhaupt nichts zu tun.
Das ist pauschal falsch.
~Foo()=default;
ist nicht user-provided, und daher ist der Destruktor trivial. Eine Definition via{}
ist nicht trivial.
-
@SeppJ Dein Beispiel mag @TGGC von seiner Totgeburt einer Idee abbringen, aber mein Vorschlag hat nichts mit einer Substitution zu tun. Definiere einfach ein compiler intrinsic, dass die Trivialität des Destruktors einer Klasse prüft. Das kann auf die Standarddefinition zurückfallen, oder etwas ausgeklügelteres tun. Es gibt keinerlei Problem dabei, ein derartiges Intrinsic zu implementieren, genausowenig wie e.g.
__builtin_constant_p
und Konsorten. Dieses Intrinsic wird dann instd::copy
eingesetzt. Nicht, dass—wie bereits erwähnt—irgendein Anreiz dafür bestünde. Aber völlig machbar.
-
@tggc sagte in Unterschied zwischen leerem Destruktor und default Desktruktor:
@hustbaer sagte in Unterschied zwischen leerem Destruktor und default Desktruktor:
@seppj Ich muss gestehen ich bin wohl zu doof für das Beispiel. Ich meine klar, im ersten Fall haben Foo und Bar beide einen nicht-trivialen Dtor und im zweiten Fall beide einen trivialen. Was offensichtlich ist, und daher denke ich mal dass es dir nicht darum geht...
Ist ganz einfach: ~Foo()=default; erzeugt nen public inline Destruktor... Mit trivial oder nicht hat das überhaupt nichts zu tun.
Naja ne, das ganze ist viel komplizierter. Der Access-Level wird bei
=default
auf jeden Fall übernommen! Undstruct X { ~X() = default; };
ist trivial zerstörbar,
struct Y { ~Y() {} };
hingeben nicht.
Weiters wird bei=default
kein Fehler generiert wenn die Funktion nicht implizit definiert werden kann, sondern die Funktion ist statt dessen einfach implicitly deleted.Im Beispiel von @SeppJ
class Foo { ~Foo()=default; }; struct Bar: public Foo { ~Bar()=default; };
hat
Foo
einen trivialen, privaten Dtor, und der Dtor vonBar
ist implicitly deleted, da der Dtor vonBar
nicht implizit definiert werden kann.Bei
class Baz { Baz(int, int) {} Baz()=default; };
hat
Baz
einen privaten, trivialen Default-Ctor -- da der Default-Ctor hier implizit definiert werden kann, wird hier die Definition mit=default
erzwungen, trotz dem ein anderer Ctor definiert wurde, und daher normalerweise kein Default-Ctor generiert würde.