2014-10-03 4 views
26

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ält T, das zugrundeliegende Bytes ([intro.memory]), um das Objekt bilden, kann in eine Anordnung von charoder kopiert werden,. Wenn der Inhalt des Arrays char oder unsigned 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.

+18

Wenn Sie 'memcpy' verwenden, ist es keine Konstruktion oder Zuweisung. –

+0

Hoffentlich schreibt TC eine Antwort, IDK, was der Status von Objekten ist, die mit 'memcpy' anstelle eines Konstruktors erstellt wurden :) –

+0

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. –

Antwort

6

Es ist für mich klar, dass die Verwendung von std::memcpy weder zu Aufbau noch Zuweisung führt.Es ist keine Konstruktion, da kein Konstruktor aufgerufen wird. Es ist auch keine Zuweisung, da der Zuweisungsoperator nicht aufgerufen wird. Da ein trivial kopierbares Objekt triviale Destruktoren, (Kopieren/Verschieben) -Konstruktoren und (Kopieren/Verschieben) Zuweisungsoperatoren hat, ist der Punkt eher irrelevant.

Sie scheinen ¶ 2 aus § 3.9 [basic.types] zitiert zu haben. Auf ¶ 3 heißt es:

Für trivialen kopierbar Typen T, wenn zwei Zeiger auf T Punkt auf verschiedene T Objekte obj1 und obj2, wo weder obj1 noch obj2 ist eine Basisklasse subobject, wenn das zugrunde liegende Bytes (1.7) makeing up obj1 werden in obj2, obj2 kopiert, soll anschließend den gleichen Wert wie obj1 halten. [Beispiel:
    T* t1p;
    T* t2p;
                    // vorausgesetzt, dasst2pzeigt auf eine initialisierte Objekt ...
    std::memcpy(t1p, t2p, sizeof(T));
                    // an dieser Stelle jeder subobject von trivialer kopierbar Typ in*t1penthält
                    // die gleiche Wert als das entsprechende Unterobjekt in*t2p
- end Beispiel]
41), indem zum Beispiel der Bibliotheksfunktionen (17.6.1.2) std::memcpy oder std::memmove.

klar, dass die Norm soll in jeder Hinsicht nutzbar ermöglichen *t1p zu sein *t2p wäre.

Fortsetzung auf ¶ 4:

Die Objektdarstellung ein Objekt des Typs T die Sequenz von N unsigned char Objekten wird durch das Objekt vom Typ aufgenommen T, wo N entspricht sizeof(T).Die Wertdarstellung eines Objekts ist die Menge der Bits, die den Wert des Typs T enthalten. Für trivial kopierbare Typen ist die Wertdarstellung eine Menge von Bits in der Objektdarstellung, die einen Wert bestimmt, der ein diskretes Element eines implementierungsdefinierten Satzes von Werten ist.
42) Die Absicht ist, dass das Speichermodell von C++ mit der ISO/IEC 9899 Programmiersprache C.

Die Verwendung des Wortes kompatibel ist die vor sowohl definierten Begriffe implizieren, dass jeder nur gegebener Typ hat ein Objektdarstellung und ein gegebenes Objekt nur ein Wertdarstellung . Ihr hypothetischer Morphing-Interntyp sollte nicht existieren. Die Fußnote stellt klar, dass trivial kopierbare Typen ein Speicherlayout haben sollen, das mit C kompatibel ist. Die Erwartung ist dann, dass selbst ein Objekt mit einem nicht standardmäßigen Layout, das es kopiert, es dennoch nutzbar machen kann.

+0

Was ist, wenn t2p nicht auf ein initialisiertes Objekt zeigt? –

+0

@MattMcNabb: Lesen von einem nicht initialisierten Objekt ist nicht definiert, ja? – jxh

+2

'Gegeben, dass ein trivial kopierbares Objekt triviale Destruktoren, Konstruktoren und Zuweisungsoperatoren hat' Nur Kopien und Verschiebungskonstruktoren müssen trivial sein. Trivial kopierbare Typen können nicht-triviale 'normale' Konstruktoren haben. Vielleicht denken Sie an PODs, die keine Konstruktoren haben können, aber eine strengere Obermenge von trivial kopierbar sind. –

2

Im gleichen Entwurf, können Sie auch den folgenden Text finden, direkt nach dem Text, den Sie zitiert:

Für trivialer kopierbar Typ T, wenn zwei Zeiger auf T Punkt auf verschiedene T Objekte obj1 und obj2, wo weder obj1 noch obj2 eine Basisklasse Subobjekt ist, wenn das zugrunde liegende Bytes (1.7) obj1 bildet in obj2 kopiert wird, wird obj2 anschließend den gleichen Wert wiehalten.

anzumerken, dass dies zu einer Veränderung des Wertes der obj2 spricht, nicht über das Objekt obj2 zerstören und ein neues Objekt an seinem Platz zu schaffen. Da nicht das Objekt, sondern nur sein Wert geändert wird, sollten alle Zeiger oder Verweise auf seine Mitglieder gültig bleiben.

+0

Dies würde bedeuten, dass "Meow_internal" keine standardkonforme Implementierung von "Meow" ist. Ich stimme dieser Interpretation zu. Die Konsequenz daraus ist jedoch, dass die Unterscheidung des Standards zwischen "trivial kopierbar" und "Standard-Layout" etwas verschwommen ist. Soweit ich das beurteilen kann, muss "offsetof" konzeptionell mit trivial kopierbaren Typen zusätzlich zu Standard-Layout-Typen arbeiten, oder die Implementierung ist nachweislich aus anderen Gründen nicht konform. – Myria