CopyCtor und Referenz
-
Abend!
Simple Frage:
Wenn eine Klasse eine Referenz als Member hat: Macht dann der vom Compiler generierte CopyCtor das richtige? Oder muss ich mir dann den CopyCtor selber schreiben?
Oder macht ein CopyCtor bei Klasse mit Referenz als Member generell keinen Sinn?
-
CopyFrager schrieb:
Wenn eine Klasse eine Referenz als Member hat
... dann ist das schlechtes Design und sollte schleunigst geändert werden. Nimm stattdessen einen Pointer.
-
Macht dann der vom Compiler generierte CopyCtor das richtige?
Ja, er initialisiert die Member-Referenz mit der anderen Referenz.
[class.copy]/15 schrieb:
The implicitly-defined copy/move constructor for a non-union class X performs a memberwise copy/move of its bases and members.
[...] Let x be [...] the parameter of the constructor [...]. Each base or non-static data member is copied/moved in the manner appropriate to its type:
— if the member is an array, each element is direct-initialized with the corresponding subobject ofx;
— if a membermhas rvalue reference typeT&&, it is direct-initialized withstatic_cast<T&&>(x.m);
— otherwise, the base or member is direct-initialized with the corresponding base or member ofx.Das ist auch das "Richtige". Oder was erwartest du?
Oder macht ein CopyCtor bei Klasse mit Referenz als Member generell keinen Sinn?
Ne, macht sie nicht. Willst du Gründe?
-
@Arcoth: Danke für die gute Antwort!
Arcoth schrieb:
Ne, macht sie nicht. Willst du Gründe?
Ja, gerne! Der Compiler erzeugt einen CopyCtor und der funktioniert auch mit Referenzen als Member. Dann sollte das doch passen?
Ich habe nur bemerkt, dass der operator= offenbar NICHT mehr vom Compiler geniert wird.
Kann es sein, dass ich bei einer Referenz als Member den operator= gar nicht sinnvoll definieren KANN? Heißt das dann Klassen mit Referenz als Member sind niemals kopierbar? (wenn operator= nicht geht, sollte ich ja auch gleich CopyCtor ausschalten, denn nur einen von beiden zu haben ist ja schlecht)
-
CopyFrager schrieb:
@Arcoth: Danke für die gute Antwort!
Arcoth schrieb:
Ne, macht sie nicht. Willst du Gründe?
Ja, gerne! Der Compiler erzeugt einen CopyCtor und der funktioniert auch mit Referenzen als Member. Dann sollte das doch passen?
Ja, wie das passt, aber wie du schon unten richtig bemerkt hast:
Ich habe nur bemerkt, dass der operator= offenbar NICHT mehr vom Compiler geniert wird.
Kann es sein, dass ich bei einer Referenz als Member den operator= gar nicht sinnvoll definieren KANN?
Korrekt, Referenzen sind nicht veränderbar.
Heißt das dann Klassen mit Referenz als Member sind niemals kopierbar? (wenn operator= nicht geht, sollte ich ja auch gleich CopyCtor ausschalten, denn nur einen von beiden zu haben ist ja schlecht)
Die Folgerung ist falsch. Es macht durchaus Sinn Klassen zu haben, die man nur einmal als Kopie von etwas erstellen kann, aber anschließend nicht ändern. Ich verwende sie von Zeit zu Zeit.
-
CopyFrager schrieb:
Kann es sein, dass ich bei einer Referenz als Member den operator= gar nicht sinnvoll definieren KANN?
Kommt auf deine Definition von sinnvoll an, aber wenn sinnvoll=zuweisen ist dann ist es richtig.
CopyFrager schrieb:
Heißt das dann Klassen mit Referenz als Member sind niemals kopierbar? (wenn operator= nicht geht, sollte ich ja auch gleich CopyCtor ausschalten, denn nur einen von beiden zu haben ist ja schlecht)
Kopieren geht. Zuweisen nicht. Es gibt bestimmt Fälle wo soetwas sinnvoll ist.
-
Heißt das dann Klassen mit Referenz als Member sind niemals kopierbar?
Doch. Aber es kann nicht die Referenz umgebogen (da das in C++ offensichtlich nicht funktioniert), sondern lediglich das Objekt, auf das verwiesen wird, zugewiesen werden.
Ich habe nur bemerkt, dass der operator= offenbar NICHT mehr vom Compiler geniert wird.
Korrekt! Das wird (u.a.) automatisch unterbunden, sobald eine Klasse eine Referenz als (nicht-statischen) Member hat:
[class.copy]/23 schrieb:
A defaulted copy/move assignment operator for class
Xis defined as deleted ifXhas:
[...]
— a non-static data member of reference type, orUnd hier die Gründe warum Referenzen als Member keinen Sinn machen, bzw. immer schlechter sind als (ggf. konstante) Zeiger:
- Wendest du den
sizeof-Operator auf eine Referenz an, gibt er dir die Größe des referenzierten Typs zurück. Tatsächlich aber ist die Größe einer Referenz meistens eine andere - bspw. die eines Zeigers.
Wendest du den
sizeof-Operator auf eine Klasse an, die eine Referenz als (nicht-statischen) Member hat, merkst du das schnell:struct A { char& m; }; #include <utility> int main() { static_assert( sizeof(std::declval<A>().m) == 1, "" ); static_assert( sizeof(std::declval<A>()) == sizeof(void*), "" ); }- Weil der Standard die interne Darstellung einer Referenz nur äußerst vage festlegt, haben Klassen mit einer Referenz als Member kein standard-layout.
- Sie sind nicht verbiegbar; Nutzt man jedoch Zeiger, kann man lediglich mit einem top-level
consttriggern, ob man den Zeiger verändern darf oder nicht. - Referenzen sind keine Objekte, folglich haben sie keine Adresse. Memberzeiger sind damit nicht möglich. (Ein eher schwaches Argument)
- Eine Referenz hat keinen Nullzustand. Das ist aber nicht wirklich ein richtiges Argument für irgendetwas, weil es gleichzeitig ein Vorteil und ein Nachteil ist - es ist mehr ein Unterschied.
Es gibt selten Fälle, wo Semantiken einer Referenz (bspw. fehlender Nullzustand, Unumbiegbarkeit) gewünscht sind, aber das sind Ausnahmen, die die Regel bestätigen.
- Wendest du den
-
Naja, ich habe bewusst eine Referenz genommen, weil sich das Objekt, das referenziert wird, IMMER existieren muss. Es kann also nie "NULL" sein - da fand ich die Referenz besser. Wenn es nie Sinn machen würde, würde es C++ ja auch kaum erlauben?
-
CopyFrager schrieb:
Wenn es nie Sinn machen würde, würde es C++ ja auch kaum erlauben?
C++ ist nicht darauf ausgelegt, Dinge zu verbieten, die nie Sinn machen würden.
Eines der Hauptphilosophien von C++ ist es, jeden Programmierstil zu erlauben.
Man kann in C++ ja auch manuell mit Speicher hantieren oder nicht virtuelle, nciht protected Destruktoren in Basisklassen haben, auch wenn das "nie" Sinn macht.