Gottlob Frege wrote:
Some days I'm like "man just accept empty, it would be a simple API", but then I think "it is stupid for variant
to be empty". Other days I think "just double buffer when necessary" (I assume that's your direction Peter?), but then I think "I don't want double-buffering variant to go on/off based on whether I use MS std vs libc++ etc" and also "I don't want double buffering for cases that only happen in theory, not in practice".
That is my direction, yes. My variant uses double storage when (1) not all
types have noexcept move constructors and (2) there isn't a noexcept default
constructible type in the list.
I think that in practice few variants will hit the double case, as it's rare
to have variant without a scalar alternative, although who
knows.
For variant
specifically, when going from libstdc++ to MS STL
the difference is that sizeof(variant) changes, but it changes for
std::variant, too. Obviously, double storage can never be as good as single
storage, but I view this as an acceptable compromise. You can guarantee
single storage by putting a nothrow default constructible type in the list
of alternatives.
- I think std::expected should always throw something deriving from std::exception. So if E derives from std::exception, we can throw it. If E is exception_ptr we can (re)throw it. If E is error_code/system_error/etc we can figure out what to throw. If E is user-type, then we wrap it in bad_expected or whatever.
My suggested expected<> just calls `throw_on_unexpected(e)` unqualified (it's a customization point.) There are overloads for std::error_code and std::exception_ptr and the fallback default is to throw bad_expected_access<E>.