Reinterpret struct with members of the same type as an array in a standard compliant way
No, it is not legal because when adding an integer to a pointer, the following applies ([expr.add]/5):
If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined.
y
occupies the memory location one past the end of x
(considered as an array with one element) so adding 1 to &x
is defined, but adding 2 to &x
is undefined.
You can never be sure that this will work
There is no guarantee of contiguity of subsequent members, even if this will frequently work perfectly in practice thanks to usual float alignment properties and permissive pointer arithmetic.
This is laid down in the following clause of the C++ standard:
[class.mem]/18: Non-static data-members (...) with the same access control are allocated so that later members have higher addresses within the class object. Implementation alignment requirements might cause two adjacent members not to be allocated after each other.
There is no way to make this legal using static_assert
nor alignas
constraints. All you can do is to prevent the compilation, when the elements are not contiguous, using the property that the address of each object is unique:
static_assert (&y==&x+1 && &z==&y+1, "PADDING in vector");
But you can reimplement the operator to make it standard compliant
A safe alternative, would be to reimplement operator[]
to get rid of the contiguity requirement for the three members:
struct vec {
float x,y,z;
float& operator[](size_t i)
{
assert(i<3);
if (i==0) // optimizing compiler will make this as efficient as your original code
return x;
else if (i==1)
return y;
else return z;
}
};
Note that an optimizing compiler will generate very similar code for both the reimplementation and for your original version (see an example here). So rather choose the compliant version.
Type aliasing (use of more then one type for essentially the same data) is a huge problem in C++. If you keep member functions out of structs and maintain them as PODs, things ought to work. But
static_assert(sizeof(vec) == sizeof(float) * 3);
can't make accessing one type as another technically legal. In practice of course there will be no padding, but C++ isn't clever enough to realise that vec is an array of floats and an array of vecs is an array of floats constrained to be a multiple of three, and the casting &vecasarray[0] to a vec * is legal but casting &vecasarray[1] is illegal.