On 31/05/2017 01:18, Niall Douglas wrote:
FWIW, my preferred options would be (which I think were the originals):
- value() throws if holding error or exception (or empty)
- error() returns E{} if holding value or E{errc::has_exception} if holding exception, or E{errc::no_value} if empty (names made up on the spot, doesn't matter)
The current behaviour is "always throw on observing empty". That's what makes empty special (and it traps unintentional propagation of empty).
That makes sense when empty is always "you forgot to return a value". However you've said yourself that sometimes you've hijacked it as an intentional return of no value (as in optional<T>), and that's where things get murkier.
- exception() returns nullptr if holding value or error or empty
The current behaviour is to return null exception_ptr if valued (so if(eptr) would be false i.e. "no exception here"), std::make_exception_ptr(std::system_error(error())) if errored, throw on empty.
So an errored state is also an excepted state, but an excepted state is never an errored state. has_exception() returns true for either errored or excepted states.
std::make_exception_ptr(std::system_error(error())) is fairly cheap, a few thousand CPU cycles. std::exception_ptr's are heavy anyway.
I can understand that, but I'm not sure I can entirely agree with it. As previously mentioned an error_code return is supposed to indicate a non-exceptional error, while an exception is exceptional. So this feels like promoting a non-serious error to a serious error. Having said that, it's not a deal-breaker; both behaviours make sense in their own way, and that behaviour could be convenient in some cases. Although... should it actually throw on empty or just return an exception_ptr containing an exception that indicates it was unexpectedly empty? The only place you should be using outcome<> rather than result<> is inside noexcept methods, so throwing is fairly serious.