Ich spiele mit [[no_unique_address]]
in herum c++20
.
Im Beispiel für cppreference haben wir einen leeren Typ Empty
und TypZ
struct Empty {}; // empty class
struct Z {
char c;
[[no_unique_address]] Empty e1, e2;
};
Anscheinend muss die Größe von Z
mindestens so sein, 2
weil die Typen von e1
und gleich e2
sind.
Allerdings möchte ich wirklich Z
mit Größe haben 1
. Dies brachte mich zum Nachdenken, was ist mit dem Umschließen Empty
einer Wrapper-Klasse mit zusätzlichen Vorlagenparametern, die verschiedene Arten von e1
und erzwingen e2
.
template <typename T, int i>
struct Wrapper : public T{};
struct Z1 {
char c;
[[no_unique_address]] Wrapper<Empty,1> e1;
[[no_unique_address]] Wrapper<Empty,2> e2;
};
Leider sizeof(Z1)==2
. Gibt es einen Trick, um die Größe Z1
eins zu machen ?
Ich teste das mit gcc version 9.2.1
undclang version 9.0.0
In meiner Bewerbung habe ich viele leere Typen des Formulars
template <typename T, typename S>
struct Empty{
[[no_unique_address]] T t;
[[no_unique_address]] S s;
};
Welches ist ein leerer Typ, wenn T
und S
sind auch leere Typen und verschieden! Ich mag diese Art leer sein , auch wenn T
und S
sind die gleichen Typen.
T
selbst? Das würde unterschiedliche Typen erzeugen. Im Moment hält Sie die Tatsache, dass beideWrapper
erbenT
, zurück ...T
? Im MomentT
ist ein Vorlagenargument.T
.Antworten:
Das kannst du nicht bekommen. Technisch gesehen kann man nicht einmal sicher , dass es auch dann , wenn leer ist
T
undS
es verschiedene Typen leer. Denken Sie daran:no_unique_address
ist ein Attribut; Die Fähigkeit, Objekte zu verbergen, ist vollständig Implementierung ab. Aus Sicht der Standards können Sie die Größe leerer Objekte nicht erzwingen.Wenn C ++ 20-Implementierungen ausgereift sind, sollten Sie dies annehmen
[[no_unique_address]]
die Regeln für die Optimierung der leeren Basis im Allgemeinen werden. Solange zwei Objekte desselben Typs keine Unterobjekte sind, können Sie wahrscheinlich damit rechnen, dass sie sich verstecken. Aber an diesem Punkt ist es eine Art Pech.Was den speziellen Fall
T
undS
den gleichen Typ betrifft, ist dies einfach nicht möglich. Trotz der Auswirkungen des Namens "no_unique_address" erfordert C ++ in der Realität, dass bei zwei Zeigern auf Objekte desselben Typs diese Zeiger entweder auf dasselbe Objekt zeigen oder unterschiedliche Adressen haben. Ich nenne dies die "eindeutige Identitätsregel" undno_unique_address
habe keinen Einfluss darauf. Aus [intro.object] / 9 :Mitglieder von leeren Typen deklariert als
[[no_unique_address]]
Null sind, haben jedoch den gleichen Typ, was dies unmöglich macht.Wenn Sie darüber nachdenken, verstößt der Versuch, den leeren Typ durch Verschachtelung auszublenden, immer noch gegen die eindeutige Identitätsregel. Betrachten Sie Ihren
Wrapper
undZ1
Fall. Bei einer ,z1
die ist eine InstanzZ1
, ist es klar , dassz1.e1
undz1.e2
verschiedene Objekte mit unterschiedlichen Typen sind. Istz1.e1
jedoch weder darin verschachteltz1.e2
noch umgekehrt. Und während sie haben verschiedene Arten,(Empty&)z1.e1
und(Empty&)z1.e2
sind nicht verschiedene Typen. Sie zeigen jedoch auf verschiedene Objekte.Und nach der eindeutigen Identitätsregel müssen sie unterschiedliche Adressen haben. Also obwohl
e1
unde2
nominell unterschiedliche Typen sind, müssen ihre Interna auch einer eindeutigen Identität gegenüber anderen Unterobjekten in demselben enthaltenden Objekt gehorchen. Rekursiv.Was Sie wollen, ist in C ++ in der jetzigen Form einfach unmöglich, unabhängig davon, wie Sie es versuchen.
quelle
Soweit ich das beurteilen kann, ist dies nicht möglich, wenn Sie beide Mitglieder haben möchten. Sie können sich jedoch spezialisieren und nur eines der Mitglieder haben, wenn der Typ gleich und leer ist:
Natürlich müsste der Rest des Programms, das die Mitglieder verwendet, geändert werden, um den Fall zu behandeln, in dem es nur ein Mitglied gibt. Es sollte in diesem Fall keine Rolle spielen, welches Mitglied verwendet wird - schließlich handelt es sich um ein zustandsloses Objekt ohne eindeutige Adresse. Die gezeigten Elementfunktionen sollten dies vereinfachen.
Sie könnten weitere Spezialisierungen einführen, um die rekursive Komprimierung leerer Paare zu unterstützen:
Noch mehr, um so etwas zu komprimieren
Empty<Empty<A, char>, A>
.quelle
sizeof(Empty<Empty<A,A>,A>{})==2
woA
eine völlig leere Struktur ist.get_empty<T>
Funktion hinzufügen . Dann können Sie dasget_empty<T>
links oder rechts wiederverwenden, wenn es dort bereits funktioniert.