4 + 8 = 12, so 12 bytes is the minimum amount of memory to fit a 4 and a 8 byte object.
However, there is another consideration besides the total number of bytes of the subobjects. Each subobject has a particular alignment requirement.
The alignment of std::string
depends on its complete subobjects, which are implementation defined. It appears, that std::string
on your implementation has to be aligned to an 8 byte boundary. As a consequence, the alignment requirement of Test1
is also 8, and there must be 4 bytes of padding after the member i
. So, the size of Test1
is the sum of the sizes of the subobjects + the pad bytes, which totals 4 + 8 + 4 = 16.
char
and consequently char[8]
have alignment requirement of 1. Therefore the alignment requirement of Test2
is 4
(because of int
member) and no padding is required after the member i
. Since there is no padding, the size of Test2
matches the sum of the sizes of the subobjects.
Another point of view is that the size must be a multiple of the alignment requirement. 16 is the smallest multiple of 8 (alignment of Test1
) that is greater than or equal to 12. 12 is the smallest multiple of 4 (alignment of Test2
) that is greater than or equal to 12.