I have the following type and my goal is to optimize the size of Storage
:
struct Inlined { int data[9]; }; // sizeof => 36
struct Allocated { int* data; size_t capacity }; // sizeof => 16
union Data { Inlined inlined; Allocated allocated; }; // sizeof => 40
struct Storage { // sizeof => 56
size_t size;
bool is_allocated;
Data data;
};
I understand that sizes and layout may vary between compilers and platforms. My goal is to get more compact layout on at least some compilers/platforms.
As you can see in my example, Data
has 4 bytes of padding because Inlined
and Allocated
have different alignments and Data
is 8-byte aligned. I am trying to find a way to squeeze boolean is_allocated
into this padding.
So far, I came up with the following structure and I would really appreciate if someone can confirm or deny "safety" / "legalness" of such type:
struct CompactStorage { // sizeof => 48
size_t size;
union {
struct {
bool is_allocated;
Inlined inlined;
};
struct {
bool copy_of_is_allocated;
Allocated allocated;
};
};
};
To clarify, by safety I mean that I expect CompactStorage::is_allocated
and CompactStorage::copy_of_is_allocated
to be aliasing the same memory. And I expect that if I write to CompactStorage::is_allocated
then it does not overwrite bytes aliased by CompactStorage::allocated
(and vice versa). If that does not hold, then I consider CompactStorage
unsafe as it cannot be used as a replacement for Storage
.
P.S. I know about bitfields and deliberately do not use them due to (proven) performance implications.
CodePudding user response:
This is very much well-defined. From [class.mem]
In a standard-layout union with an active member of struct type T1, it is permitted to read a non-static data member m of another union member of struct type T2 provided m is part of the common initial sequence of T1 and T2; the behavior is as if the corresponding member of T1 were nominated.
Where common initial sequence is
The common initial sequence of two standard-layout struct types is the longest sequence of non-static data members and bit-fields in declaration order, starting with the first such entity in each of the structs, such that corresponding entities have layout-compatible types [...]
Which means you may use is_allocated
and copy_of_is_allocated
interchangeably, given that the structs are standard-layout types.