Discrepancy in C++ between over-aligned struct and enum within container
This is C++ core working group issue 2354, which was recently resolved by removing the permission to apply alignas
to an enum
type. (At the time of writing, the latest public version of the issues list doesn't contain the resolution, but you can find the resolution in P1359R0, which was adopted into the C++ working draft in February 2019 and accepted as a Defect Report (which means the fix is intended to apply retroactively).
The problem is that we had two conflicting requirements:
an enum-base (including the implicit enum-base of
int
in a scoped enumeration) specifies the underlying type of the enumeration, and the enumeration is required to have the same object representation (includingsizeof
and representation of all values) as its underlying type, andan alignment-specifier specifies the alignment of the type, which in turn must also constrain
sizeof(E)
(which is by definition the distance between two objects of typeE
in an array) to a multiple of said alignment.
You can't have both, so we resolved the conflict by removing the ability to specify an alignment on an enumeration type.
The best advice is to not apply an alignment specifier to an enumeration type; implementations will stop accepting that at some point. (Applying the alignment to a use of the type in the declaration of a variable or non-static data member is OK, though.)
Yes, this seems to be a bug.
According to the standard - 9.11.2:
1 An alignment-specifier may be applied to a variable or to a class data member, but it shall not be applied to a bit-field, a function parameter, or an exception-declaration (13.3). An alignment-specifier may also be applied to the declaration of a class (in an elaborated-type-specifier (9.1.7.3) or class-head (Clause 10), respectively) and to the declaration of an enumeration (in an opaque-enum-declaration or enum-head, respectively (9.6)). An alignment-specifier with an ellipsis is a pack expansion (12.6.3).
enums should be aligned as well.
UBSan also complains:
/usr/include/c++/8.3.0/ext/new_allocator.h:136: runtime error: store to misaligned address 0x602000000031 for type 'byte_enum', which requires 16 byte alignment
0x602000000031: note: pointer points here
00 80 58 be be 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
^
/usr/include/c++/8.3.0/bits/stl_iterator.h:797:17: runtime error: reference binding to misaligned address 0x602000000031 for type 'byte_enum', which requires 16 byte alignment
0x602000000031: note: pointer points here
00 80 58 01 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
^
/usr/include/c++/8.3.0/bits/stl_vector.h:1033:20: runtime error: reference binding to misaligned address 0x602000000031 for type 'value_type', which requires 16 byte alignment
0x602000000031: note: pointer points here
00 80 58 01 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
^
0x602000000050
0x602000000051
0x602000000052
That might be a libstdc++ std::vector
bug though because using an array runs fine with UBSan:
{//with enum
std::array<byte_enum, 3> bytes = { byte_enum{1}, byte_enum{2}, byte_enum{3} };
for(auto it = bytes.begin(); it!= bytes.end(); ++it) {
std::cout<<&*it<<std::endl;
}
}