Betrachten Sie die folgenden drei struct
s:
class blub {
int i;
char c;
blub(const blub&) {}
};
class blob {
char s;
blob(const blob&) {}
};
struct bla {
blub b0;
blob b1;
};
Auf typischen Plattformen mit int
4 Bytes sind die Größen, Ausrichtungen und die Gesamtauffüllung 1 wie folgt:
struct size alignment padding
-------- ------ ----------- ---------
blub 8 4 3
blob 1 1 0
bla 12 4 6
Es gibt keine Überlappung zwischen der Lagerung der blub
und der blob
Elemente, obwohl die Größe 1 blob
im Prinzip in die Polsterung von "passen" könnte blub
.
In C ++ 20 wird das no_unique_address
Attribut eingeführt, mit dem benachbarte leere Mitglieder dieselbe Adresse verwenden können. Es erlaubt auch explizit das oben beschriebene Szenario, das Auffüllen eines Mitglieds zum Speichern eines anderen zu verwenden. Aus der Referenz (Hervorhebung von mir):
Gibt an, dass dieses Datenelement keine Adresse haben muss, die sich von allen anderen nicht statischen Datenelementen seiner Klasse unterscheidet. Dies bedeutet, dass der Compiler einen leeren Typ (z. B. zustandslosen Allocator) möglicherweise so optimiert, dass er keinen Speicherplatz belegt, genau wie wenn es sich um eine leere Basis handelt. Wenn das Element nicht leer ist, kann das darin enthaltene Tail-Padding auch zum Speichern anderer Datenelemente wiederverwendet werden.
In der Tat, wenn wir dieses Attribut auf verwenden blub b0
, bla
fällt die Größe der Tropfen auf 8
, so dass die blob
in der Tat in der blub
wie auf Godbolt gesehen gespeichert ist .
Zum Schluss kommen wir zu meiner Frage:
Welcher Text in den Standards (C ++ 11 bis C ++ 20) verhindert diese Überlappung ohne no_unique_address
für Objekte, die nicht trivial kopierbar sind?
Ich muss trivial kopierbare (TC) Objekte von den oben genannten ausschließen, da es für TC-Objekte zulässig ist, std::memcpy
von einem Objekt zum anderen zu wechseln, einschließlich der Unterobjekte der Mitglieder, und wenn sich der Speicher überlappt, würde dies brechen (weil der gesamte oder ein Teil des Speichers für das benachbarte Mitglied würde überschrieben werden) 2 .
1 Wir berechnen die Polsterung einfach als Differenz zwischen der Strukturgröße und der Größe aller ihrer Bestandteile rekursiv.
2 Aus diesem Grund habe ich Kopierkonstruktoren definiert: zu machen blub
und blob
nicht trivial kopierbar .