https://gcc.gnu.org/bugzilla/show_bug.cgi?id=98978
Bug ID: 98978 Summary: Consider packing _M_Engaged in the tail padding of T in optional<> Product: gcc Version: 11.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: libstdc++ Assignee: unassigned at gcc dot gnu.org Reporter: andysem at mail dot ru Target Milestone: --- Using std::optional with some types may considerably increase object sizes since it adds alignof(T) bytes worth of overhead. Sometimes it is possible to avoid this overhead if the flag indicating presence of the stored value (_M_Engaged in libstdc++ sources) is placed in the tail padding of the T object. This can be done if std::optional constructs an object of a type that derives from T, which has an additional bool data member that is initialized to true upon construction. The below code roughly illustrates the idea: template< typename T > struct _Optional_payload_base { struct _PresentT : T { const bool _M_Engaged = true; // Forwarding ctors and other members }; static constexpr size_t engaged_offset = offsetof(_PresentT, _M_Engaged); struct _AbsentT { unsigned char _M_Offset[engaged_offset]; const bool _M_Engaged = false; }; union _Storage { _AbsentT _M_Empty; _PresentT _M_Value; _Storage() : _M_Empty() {} // Forwarding ctors and other members }; _Storage _M_payload bool is_engaged() const noexcept { return *reinterpret_cast< const bool* >(reinterpret_cast< const unsigned char* >(&_M_payload) + engaged_offset); } }; The above relies on some implementation details, such as: - offsetof works for the type T. It does for many types in gcc, beyond what is required by the C++ standard. Maybe there is a way to avoid offsetof, I just didn't immediately see it. - The location of _M_Engaged in both _PresentT and _AbsentT is the same. This is a property of the target ABI, and AFAICS it should be true at least on x86 psABI and I think Microsoft ABI. The above will only work for non-final class types, for other types, and where the above requirements don't hold true, the current code with a separate _M_Engaged flag would work.