F
Hi nochmal!
Die Sache mit dem Standard Layout ist Bockmist, da ich eine wichtige Regel überlesen habe: B ist nicht Standard Layout,
da für seinen Ableitungspfad in mehr als einer Klasse nicht-statische Daten-Member deklariert werden.
Ich bin mittlerweile auch zu dem Schluss gekommen, dass der Standard wohl keine zusammenhängenden T s garantiert,
und glaube dass die Argumentation, weshalb dieser Code allgemein funktioniert, wesentlich trivialier und "hackiger" ist,
als ich zunächst angenommen habe:
first_element und other_elements sind in der eigentlichen Implementierung nicht vom Typ T , sondern de factor char-Arrays
mit korrektem Alignment, in deren Speicher die eigentlichen Objekte bei Bedarf konstruiert werden (ähnlich std::aligned_storage ).
Das bedeutet, dass man selbst wenn Padding stattfindet, ab Adresse &first_element genügend Speicher vorhanden ist, um das
Array aufzunehmen. Das gilt allerdings nur, wenn im Layout von B das Objekt other_elements direkt auf first_element folgt,
und das ist gemäß Standard explizit nicht spezifiziert:
10.0.5
The order in which the base class subobjects are allocated in the most derived object (1.8) is unspecified. [...]
Anmerkung: mit "base class subobjects" sind die gesamten Basisklassen als Objekte gemeint, und nicht etwa nur die Member der Basisklassen.
Tatsächlich gibt der Standard nur sehr wenige Zusicherungen her, was ds Speicherlayout allgemeiner Klassen angeht.
Interessant ist allerdings dieser Punkt:
9.2.13
Nonstatic data members of a (non-union) class with the same access control (Clause 11) are allocated so
that later members have higher addresses within a class object. The order of allocation of non-static data
members with different access control is unspecified (Clause 11). Implementation alignment requirements
might cause two adjacent members not to be allocated immediately after each other; so might requirements
for space for managing virtual functions (10.3) and virtual base classes (10.1).
Daraus ziehe ich folgende Schlüsse:
first_element hat innerhalb von A (und somit auch im A -Teil von B ) die höchste Adresse, ist also das letzte Objekt in A (da alle Daten-Member mit dem selben Zugriffsmodifikator)
- zwischen den Daten-Membern können höchstens Padding-Bytes liegen (da keine virtuelle Vererbung)
So wie ich das sehe, bleibt also nur noch die nicht-garantierte Annahme, dass für B immer &first_element < &other_elements[0] gilt, wovon ich
vermute, dass das bei gängigen Compilern der Fall sein wird (lässt sich ggfs. via static_assert prüfen). Das ist nicht so sauber wie ich erhofft hatte,
aber zumindest etwas mit dem ich experimentieren würde, sollten es die Vorteile es rechtfertigen.
Finnegan