c++standardsanonymousmemory-layoutstandard-layout

Anonymous Unions in C++ Contiguous Memory Layout


struct Vec0
{
    union { float x, r, h; };
    union { float y, g, s; };
    union { float z, b, v; };
};

I wanted to ask, does the C++ standard guarantee this data will be aligned exactly the same as if I wrote it

struct Vec1
{
    float x, y, z;
};

The address y immediately follows the address of x plus the length for a float + padding bits/alignment (I think).

Think it does (guarantee the layout), but was curious if it is guaranteed by the ISO C++ Standard?

Update:

To clarify, I am interested in the question, is there any guarantee in the ISO C++ standard that

offsetof(Vec0, x /*or r h*/) == offsetof(Vec1, x)
offsetof(Vec0, y /*or g s*/) == offsetof(Vec1, y)
offsetof(Vec0, z /*or b v*/) == offsetof(Vec1, z)

This may fall into the category of them being layout compatible

I am also interested in the sub-case of whether or not the offset of (but not necessarily offsetof because one may not be able to use offsetof outside the struct/class/etc., perhaps except in the hypothetical were it were able to access private/protected data members outside the struct/class/etc.) each data member would be the same between the two if all their data-members were made private/protected.

Also just to note this is not a software design/architecture/best practice question, I am aware there are better ways to access these data members for most cases.


Solution

  • Think it does, but was curious if it is guaranteed by the ISO C++ Standard?

    It is not, there can be any arbitrary padding between the union or float members that doesn't have to be consistent between the two examples. This holds in ISO standard C++ as well as ISO standard C.

    However, if there is no good reason to add such padding, there wouldn't be any point to it being added. The class layout is part of the ABI and therefore specified in the specification of the C++ ABI or underlying C ABI that your compilation is assuming. You can get definitive answers for your particular compiler/platform combination in the corresponding ABI specification.


    After edit of question:

    offsetof(Vec0, x /*or r h*/) == offsetof(Vec1, x)
    

    is guaranteed for all choices, because Vec0 and Vec1 are standard-layout in your example. Therefore all of of these offsets are required to be exactly 0.

    On the other hand

    offsetof(Vec0, y /*or g s*/) == offsetof(Vec1, y)
    offsetof(Vec0, z /*or b v*/) == offsetof(Vec1, z)
    

    are not guaranteed in any case by the C++ standard. The padding after the first (and following) members can be different between the two classes, although I don't see any good reason for the ABI to make that choice in your specific example.

    This may fall into the category of them being layout compatible

    Vec0 and Vec1 are not layout-compatible. It already fails the listed requirements in your link because a single non-class member like Vec1::x can never be layout-compatible with a (union) class member like the first anonymous union of Vec0.

    Note that types not being layout-compatible does not mean that their members won't have same layout. It just specifies one specific condition under which they effectively have to. The layout-compatibility condition is only relevant for one very specific usage of the class, i.e. when both appear as members of a union, in which case there is an exception to the active member rules for that union. That's the only situation in which layout-compatibility has any relevance in the language.

    I am also interested in the sub-case of weather or not the offset of (but not necessarily offsetof because one may not be able to use offsetof outside the struct/class/etc., perhaps except in the hypothetical were it were able to access private/protected data members outside the struct/class/etc.) each data member would be the same between the two if all there data-members were made private/protected.

    If not all of the members have the same accessibility (private/protected/public), then the class stops being a standard-layout class and even the guarantee that the first members are at offset zero doesn't hold anymore.