Also, to construct some object, it takes time. error_code is just two words, but why waste time on setting them if you will be overriding them in the course of the function call.
Overly focusing on outcome<>'s runtime performance is odd given that it's usually returned by functions that take millions of cycles to do their job.
That may not be the case in some use cases. I've tried very hard to make Outcome's runtime overhead statistically indistinguishable from C integer error code returns (see FAQ for graph).
But either way, it's possible to optimize the default construction to still set a bit and only create the error_code on demand when it's retrieved.
Outcome does do this, but in a different situation. If you try to retrieve a .error() from an excepted Outcome, you get back an error_code of monad_errc::exception_present with category monad_category.
And, in the current state you can perform a loss-less conversion (or "upgrade") form `option<T>` to `outcome<T>`.
option<T> is only present because of the empty state; no empty state, no option<T>. Duplicating opional<T> is questionable, upconverting it to outcome<T> is, too. If you call a function that optionally gives you an object T, and you have to return outcome<T>, you need to provide a reason for the missing T, and the silent upconversion allows you to punt. Error context is lost because only you know why your implementation detail failed to provide the T you asked of it.
I think you might be underestimating the bug detecting value of an
outcome<T> having an unhandled empty state. It traps failure to handle
option<T> correctly very nicely.
Also, don't forget option<T> is the only one from result<T> and
outcome<T> which is usable in constexpr programming. One therefore ends
up using option<T> or expected