'How to structure data without bloating the size because of alignment?
Lets say we have a struct Original
as this:
class Original
{
int x;
bool y;
bool z;
};
Due to alignment, the sizeof(Original)
is 8 bytes. 4 for the int, 2 for the bools, and 2 alignment "hole".
But when i decide, that I want to structure my code, and for some reason, x
and y
should go together, I might want to refactor this structure into this:
class A
{
int x;
bool y;
};
class Original
{
A a;
bool y;
};
The Original class contains still the same stuff, but suddenly its sizeof(Original)
went from 8 bytes to 12 bytes, because sizeof(A)
is 8 bytes (4 + 1, aligned to 8), and (8 + 1) is aligned again to 12.
Since this is hardly and rarely acceptable, as we are throwing away memory space and performance, this leads us to have big and ugly structures whenever performance is relevant.
I thought that alignas might solve it, but aligignas(1)
on the A didn't change anything.
Is there a solution to this?
Solution 1:[1]
You cannot avoid padding of the class to satisfy the alignment of int
(within limits of the C++ standard).
You can potentially avoid the extra padding from alignment of intermediate sub object by making it a "potentially overlapping subobject". Being a potentially overlapping sub object allows the padding to be reused for other sub objects.
This can be achieved for by either making it a base sub object instead of a data member, or by using the no_unique_address
attribute.
struct Original : A
{
bool z;
};
struct Original
{
[[no_unique_address]] A a;
bool z;
};
However, it's still up to the language implementation whether it will overlap the potentially overlapping sub objects. For example, the Itanium ABI will only do so if the sub object type is "non-POD" as defined by the ABI. This can be achieved for example by using non-public access specifiers (members in your example are private, so this is satisfied), or by declaring a constructor:
struct A
{
protected:
int x;
bool y;
};
struct A
{
int x;
bool y;
A(const A&) = default;
};
Solution 2:[2]
If you use inheritance the compiler can place the added member variables in the padding:
#include <iostream>
class A
{
int x;
bool y;
};
class Original : public A
{
bool z;
};
int main() {
std::cout << sizeof(Original) << std::endl;
}
Output: 8
Note: if you make all members and inheritance public then Original will even be an aggregate type.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | Goswin von Brederlow |