This is precisely why I added a formal empty state, and default initialised to that. Because it causes behaviour different to valued or errored, it **very** effectively traps logic errors during code development. On **many** occasions it has successfully illuminated poorly thought through code that I have written by bringing to my attention - early and very obviously - that someone was very wrong. I am absolutely convinced it is a great design choice.
My worry with the formal empty state is precisely that as neither a value nor an error, someone might fail to check for it in code and then proceed under a false assumption. (If not has_error() then this void method must have succeeded, and I don't call value() because it was void.)
Provided that .value() and .error() both throw if called in the empty state (which I assume is the case), that helps mitigate a large part of that, but not entirely, as in the case above.
Both do throw on empty, yes. The only other time Outcomes throw is on .value() where the state is errored, in this case we throw the error directly if it's excepted or the error code wrapped into std::system_error. Regarding the danger of proceeding under a false assumption, it can't happen if users never create an empty outcome because outcomes don't suddenly turn empty on their own, so if you never create one, you'll never get one. That's why I think the formal empty state is safe. If you never create an empty outcome, you can safely totally ignore empty as a possible state. It will *NEVER* happen.
Yes, it's a misuse of the type, but it's one that I can see being very likely to happen in the real world. Even if the possibility of an empty state is heralded with bold blinking all-caps on all doc pages.
I think the docs does need to emphasise that if you never create an empty outcome, you can safely ignore ever dealing with the empty state.
I'd therefore be happier with default construction giving uninitialised contents, or a default constructed T or E. No overloading state of E.
I do like the idea of a non-default-constructed error code, because failure to initialise the result does seem like an error to me. Niall points out that this is harder to detect and treat specially in code but I don't agree with that; as long as a suitably unique error code is used then a simple assert in the error path would pick it up, no problem.
I am strongly opposed to this design. It conflates logic errors in the code design with runtime errors. It's a bad design choice.
If the consensus is that an initial non-default error code is not satisfactory, then a formal empty state seems to me like the least worst alternative. I just know that it's going to bite someone at some point.
I have been persuaded by argument here that default construction to empty is in fact a defect in the design. The formal empty state ought to *always* be explicitly constructed, and **never** occur implicitly. I have logged this defect to https://github.com/ned14/boost.outcome/issues/44. Still another option is that if T has a default constructor, we default construct to a T instance, and if T does not have a default constructor, we implement no default constructor. This is what Expected already does incidentally. Outcome could do the same. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/