@nprofile1q... Thanks (I thought you might know the answer!). It turns out unions in C++ are more like classes than I thought (I'd not really used them before today, I thought they were more like enums).
It turns out that unions in C++ can have constructors. If you explicitly define constructors from each of the types, then you get the same behaviour, so it isn't actually required (though, I note, in the two places in FreeBSD headers that use the attribute, we're not doing it).
My use case was replacing something that's forwarded via multiple layers of APIs (where most just forward it to the next one) with a union, so overloads aren't useful because they'd cause a lot of code duplication (the intermediate layers would need to know which one to forward to).
Consumers of the low-level APIs can now continue working unmodified, but can also adopt the new union in their APIs to expose both options to higher-level things.