Unterschied zwischen leerem Destruktor und default Desktruktor
-
@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.
-
@c-olumbo sagte in Unterschied zwischen leerem Destruktor und default Desktruktor:
@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.Mir fällt halt auf, dass es keine Intrinsics gibt, die etwas inhaltliches über den Code einer anderen Funktion aussagen. Ich habe es gerade mal probiert:
__builtin_constant_p funktioniert nicht einmal mit expliziten constexpr, geschweige denn mit konstanten Funktionsergebnissen, die nicht als constexpr markiert wurden.edit: Ok, es funktioniert mit constexpr, wenn man Optimierungen anschaltet. Aber der Compiler ist (von mir nicht unerwartet) nicht in der Lage, dies an nicht explizit markierten Funktionen durchzuführen, selbst wenn der Code ihm vollständig vorliegt und die Funktion trivial ist.Die Gründe dafür sind sicherlich ähnlich zu denen, die ich oben angegeben habe. Mit so einem Marker wie constexpr ist das für den Compiler einfach durchführbar und er kann es dann beim compilieren der Funktion prüfen. Aber selbstständig den Code einer anderswo definierten Funktion inhaltlich analysieren? Das ist (noch) nicht möglich.
#include <iostream> int foo() { return 11; } constexpr int bar() { return 22; } int main() { std::cout << __builtin_constant_p(foo()) << '\n' << __builtin_constant_p(bar()) << '\n'; }
-
@seppj sagte in Unterschied zwischen leerem Destruktor und default Desktruktor:
Aber selbstständig den Code einer anderswo definierten Funktion inhaltlich analysieren? Das ist (noch) nicht möglich.Bitte auch bedenken das gerade fuer eine Optimierung der allgemeine Fall nicht geloest werden muss. Man kann da problemlos sagen, wenn "anderswo" zu weit weg ist oder der Inhalt ein unentscheidbares Konstrukt enthält, bleibt man bei der alten Lösung.
Ein Beispiel wäre -Wsuggest-attribute=pure, das könnte man automatisieren, auch wenn es nicht alle Pure Funktionen findet. Es darf nur keinesfalls ein false positive geben.
-
@tggc sagte in Unterschied zwischen leerem Destruktor und default Desktruktor:
Bitte auch bedenken das gerade fuer eine Optimierung der allgemeine Fall nicht geloest werden muss. Man kann da problemlos sagen, wenn "anderswo" zu weit weg ist oder der Inhalt ein unentscheidbares Konstrukt enthält, bleibt man bei der alten Lösung.
Es existiert aber auch kein einziger Fall, wo der vereinfachte Fall in irgendeiner Art und Weise gelöst wurde! Die Schwierigkeiten, die ich für eine korrekte Lösung sehe, habe ich erklärt. Die Probleme der von dir vorgeschlagenen naiven Lösung wurden in Beispielen belegt.
Wenn man so etwas möchte, bräuchte man optimalerweise einen Marker in der Art von noexcept oder constexpr:
~Foo() empty_func { }
Das ist aber offensichtlich ein vollkommen überflüssiges Tag, denn dann kann man ja gleich =default schreiben (oder die Funktion weglassen).
-
D.h. du stimmst also meiner urpsrünglichen Aussage zu.
-
@tggc sagte in Unterschied zwischen leerem Destruktor und default Desktruktor:
D.h. du stimmst also meiner urpsrünglichen Aussage zu.
Welche ist das?
-
@seppj sagte in Unterschied zwischen leerem Destruktor und default Desktruktor:
@c-olumbo sagte in Unterschied zwischen leerem Destruktor und default Desktruktor:
@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.Mir fällt halt auf, dass es keine Intrinsics gibt, die etwas inhaltliches über den Code einer anderen Funktion aussagen. Ich habe es gerade mal probiert:
__builtin_constant_p funktioniert nicht einmal mit expliziten constexpr, geschweige denn mit konstanten Funktionsergebnissen, die nicht als constexpr markiert wurden.edit: Ok, es funktioniert mit constexpr, wenn man Optimierungen anschaltet. Aber der Compiler ist (von mir nicht unerwartet) nicht in der Lage, dies an nicht explizit markierten Funktionen durchzuführen, selbst wenn der Code ihm vollständig vorliegt und die Funktion trivial ist.Die Gründe dafür sind sicherlich ähnlich zu denen, die ich oben angegeben habe. Mit so einem Marker wie constexpr ist das für den Compiler einfach durchführbar und er kann es dann beim compilieren der Funktion prüfen. Aber selbstständig den Code einer anderswo definierten Funktion inhaltlich analysieren? Das ist (noch) nicht möglich.
Warum soll das nicht möglich sein? Mit welcher Implementierung bist du so vertraut, dass Du dir solche Aussagen zutraust? Ist dir außerdem eigentlich klar, dass
constexpr
nicht bedeutet, dass man die Funktion nicht weiter zu betrachten braucht?constexpr int f(int a) {return a? a*2 : throw;} static_assert(__builtin_constant_p(f(1))); static_assert(!__builtin_constant_p(f(0)));
-
@c-olumbo sagte in Unterschied zwischen leerem Destruktor und default Desktruktor:
Warum soll das nicht möglich sein?
Möglich ist gewiss vieles, aber halt schwierig. Du hast weiter oben schön beschrieben, wie so etwas zu einer wilden Iteration der Übersetzungsphasen führen würde, bis es irgendwann gegen einen stabilen Zustand konvergiert. Ich sehe irgendwie nicht, wie deine in dem Beitrag angesprochene Vereinfachung praktikabel funktionieren sollte.
-
Das aus der Ausgabe eines Compilers nicht folgt, dass fuer alle Compiler, alle Maschinen und allen Zeiten im gilt, das alle der eine Maschinencode immer anders und schneller ist als der andere. Das also die Angabe im Buch Allgemeingueltigkeit besitzt.
-
@tggc sagte in Unterschied zwischen leerem Destruktor und default Desktruktor:
Das aus der Ausgabe eines Compilers nicht folgt, dass fuer alle Compiler, alle Maschinen und allen Zeiten im gilt, das alle der eine Maschinencode immer anders und schneller ist als der andere. Das also die Angabe im Buch Allgemeingueltigkeit besitzt.
Die Aussage ist so schwammig und allgemein, das sie keinen Inhalt hat. Dem kann ich zustimmen, aber meine Zustimmung dazu ist wertlos, weil die Aussage wertlos ist.