Die Optimierung der leeren Basis ist großartig. Es gibt jedoch die folgende Einschränkung:
Eine leere Basisoptimierung ist verboten, wenn eine der leeren Basisklassen auch der Typ oder die Basis des Typs des ersten nicht statischen Datenelements ist, da die beiden Basisunterobjekte desselben Typs unterschiedliche Adressen innerhalb der Objektdarstellung haben müssen vom am meisten abgeleiteten Typ.
Beachten Sie den folgenden Code, um diese Einschränkung zu erklären. Das static_assert
wird scheitern. Wenn Sie entweder Foo
oder ändern, Bar
um stattdessen zu erben, Base2
wird der Fehler abgewendet:
#include <cstddef>
struct Base {};
struct Base2 {};
struct Foo : Base {};
struct Bar : Base {
Foo foo;
};
static_assert(offsetof(Bar,foo)==0,"Error!");
Ich verstehe dieses Verhalten vollständig. Was ich nicht verstehe, ist, warum dieses besondere Verhalten existiert . Es wurde offensichtlich aus einem Grund hinzugefügt, da es sich um eine explizite Ergänzung handelt, nicht um ein Versehen. Was ist ein Grund dafür?
Insbesondere, warum sollten die beiden Basis-Unterobjekte unterschiedliche Adressen haben müssen? Oben Bar
ist ein Typ und foo
eine Mitgliedsvariable dieses Typs. Ich verstehe nicht, warum die Basisklasse von Bedeutung Bar
für die Basisklasse des Typs foo
ist oder umgekehrt.
In der Tat würde ich, wenn überhaupt, erwarten, dass &foo
dies mit der Adresse der Bar
Instanz übereinstimmt, die es enthält - wie es in anderen Situationen erforderlich ist (1) . Schließlich mache ich nichts Besonderes mit virtual
Vererbung, die Basisklassen sind trotzdem leer, und die Kompilierung mit Base2
zeigt, dass in diesem speziellen Fall nichts kaputt geht.
Aber diese Argumentation ist eindeutig falsch, und es gibt andere Situationen, in denen diese Einschränkung erforderlich wäre.
Angenommen, die Antworten sollten für C ++ 11 oder neuer sein (ich verwende derzeit C ++ 17).
(1) Hinweis: EBO wurde in C ++ 11 aktualisiert und wurde insbesondere für StandardLayoutType
s obligatorisch (obwohl Bar
oben nicht a StandardLayoutType
).
Base *a = new Bar(); Base *b = a->foo;
mita==b
, abera
undb
sind eindeutig unterschiedliche Objekte (möglicherweise mit unterschiedlichen Überschreibungen virtueller Methoden).