Nehmen wir an, Sie haben ein Objekt vom Typ T
und einen geeignet ausgerichteten Speicherpuffer alignas(T) unsigned char[sizeof(T)]
. Wenn Sie std::memcpy
verwenden, um vom Objekt vom Typ T
in das Array unsigned char
zu kopieren, wird dies als Kopieraufbau oder Kopierauftrag betrachtet? Ist memcpy einer trivial kopierbaren Konstruktion oder Zuweisung?
Wenn ein Typ trivialer kopierbar ist aber nicht Standard-Layout, ist es denkbar, dass eine Klasse wie folgt aus:
struct Meow
{
int x;
protected: // different access-specifier means not standard-layout
int y;
};
wie diese umgesetzt werden könnte, da der Compiler in nicht gezwungen ist Standard mit -layout:
struct Meow_internal
{
private:
ptrdiff_t x_offset;
ptrdiff_t y_offset;
unsigned char buffer[sizeof(int) * 2 + ANY_CONSTANT];
};
der Compiler könnte speichern x
und y
von Meow innerhalb des Puffers an jedem Abschnitt von buffer
, möglicherweise sogar zu einem zufälligen Versatz innerhalb buffer
, solange sie richtig ausgerichtet ist und überlappen Sie nicht. Der Offset von x
und y
könnte sogar zufällig mit jeder Konstruktion variieren, wenn der Compiler dies wünscht. (x
könnte nach y
gehen, wenn der Compiler wünscht, weil die Norm nur Mitglieder derselben zugriffs Spezifizierer erfordert, um zu gehen, und x
und y
haben unterschiedliche Zugriffsbezeich.)
Diese Anforderungen erfüllen würde trivialer des Seins -kopierbar; Ein memcpy
würde die versteckten Offset-Felder kopieren, so dass die neue Kopie funktionieren würde. Aber einige Dinge würden nicht funktionieren. Zum Beispiel würde einen Zeiger auf x
über einen Halt memcpy
brechen:
Meow a;
a.x = 2;
a.y = 4;
int *px = &a.x;
Meow b;
b.x = 3;
b.y = 9;
std::memcpy(&a, &b, sizeof(a));
++*px; // kaboom
Allerdings ist der Compiler wirklich eine trivialen kopierbar Klasse auf diese Weise erlaubt zu implementieren? Die Dereferenzierung px
sollte nur undefiniert sein, wenn die Lebensdauer von a.x
abgelaufen ist. Hat es? Die relevanten Teile des Normentwurfs N3797 sind zu diesem Thema nicht sehr klar. Dies ist Abschnitt [basic.life]/1:
Die Lebensdauer eines Objekts eine Laufzeit-Eigenschaft des Objekts ist. Ein Objekt soll nicht-triviale Initialisierung haben, wenn es eine Klasse oder Aggregattyp ist und es oder eines seiner Mitglieder durch einen Konstruktor anders als ein trivialer Standardkonstruktor initialisiert wird. [Hinweis: Initialisierung durch eine triviale Kopie/Move-Konstruktor ist nicht-trivial Initialisierung. - Endnote] Die Lebensdauer eines Objekts des Typs
T
beginnt, wenn:
- Lagerung mit der richtigen Ausrichtung und Größe für den Typ
T
erhalten wird, und- wenn das Objekt nicht-triviale Initialisierung, Die Initialisierung ist abgeschlossen.
Die Lebensdauer eines Objekts des Typs
T
endet, wenn:
T
wenn ein Klassentyp mit einer nicht-trivialen destructor ist ([class.dtor]), die Destruktoraufrufs beginnt, oder- der Speicher, den das Objekt belegt, wird wiederverwendet oder freigegeben.
Und das ist [basic.types]/3:
Für jedes Objekt (außer einer Basisklasse subobject) von trivialen kopierbar Typ
T
, ob die Objekt einen gültigen Wert von Typ hältT
, das zugrundeliegende Bytes ([intro.memory]), um das Objekt bilden, kann in eine Anordnung vonchar
oder kopiert werden,. Wenn der Inhalt des Arrayschar
oderunsigned char
in das Objekt zurückkopiert wird, muss das Objekt anschließend seinen ursprünglichen Wert behalten. Beispiel weggelassen
Die Frage ist dann, ist ein Überschreiben einer memcpy
trivially-kopierbaren Klasseninstanz „copy Konstruktion“ oder „copy-Assignment“? Die Antwort auf die Frage scheint zu entscheiden, ob Meow_internal
ein gültiger Weg für einen Compiler ist, die trivial kopierbare Klasse Meow
zu implementieren.
Wenn memcpy
"Konstruktion kopieren" lautet, lautet die Antwort, dass Meow_internal
gültig ist, da die Konstruktion der Kopie den Speicher erneut verwendet. Wenn memcpy
"Kopierzuweisung" ist, lautet die Antwort, dass Meow_internal
keine gültige Implementierung ist, da die Zuweisung die Zeiger auf die instanziierten Member einer Klasse nicht ungültig macht. Wenn memcpy
beides ist, habe ich keine Ahnung was die Antwort ist.
Wenn Sie 'memcpy' verwenden, ist es keine Konstruktion oder Zuweisung. –
Hoffentlich schreibt TC eine Antwort, IDK, was der Status von Objekten ist, die mit 'memcpy' anstelle eines Konstruktors erstellt wurden :) –
Auf Maschinen mit sizeof (int) = 4 ist sizeof (Meow) normalerweise 8. While sizeof (Meow_internal) ist mindestens 16. Niemand würde einen solchen Compiler wegen der zusätzlichen Speichernutzung verwenden. –