status_result
and status_outcome
status_result
and status_outcome
are type aliases to basic_result<T, E, NoValuePolicy>
and basic_outcome<T, EC, EP, NoValuePolicy>
in the usual way, but
with a defaulted NoValuePolicy
which selects on the basis of status_code<DomainType>
instead.
If the E
type is not some status_code<>
, the default policy selector
will complain.
The specifications are:
experimental::status_result<T, E = experimental::error>
experimental::status_outcome<T, E = experimental::error, EP = std::exception_ptr>
So, the default E
is the erased errored status code system_code
, which can represent
any generic_code
, posix_code
, win32_code
, nt_code
, com_code
and many
other integer error and status
codings. Because it is an errored status code, it will always represent a failure.
You can absolutely choose an E
type which is non-erased e.g. posix_code
directly.
You can also choose an E
type which is not contract guaranteed to be a failure,
though your users may find that surprising.
Whether to choose typed status codes versus the erased status codes depends on your
use cases. Outcome replicates faithfully the implicit and explicit conversion
semantics of its underlying types, so you can mix results and outcomes of
<system_error2>
types exactly as you can the <system_error2>
types themselves
e.g. typed forms will implicitly convert into erased forms if the source type
is trivially copyable or move relocating. This means that you can return a
generic_code
from a function returning a system_code
or error
, and it’ll
work exactly as you’d expect (implicit conversion).
As status_code<erased<T>>
is move-only, so is any status_result
or status_outcome
.
For some reason this surprises a lot of people, and they tend to react by not using the erased
form because it seems “difficult”.
It is actually, in fact, a wise discipline to follow to make all functions return
move-only types if you care about determinism and performance. Whilst C++ 17 onwards
does much to have the compiler avoid copying of identical function return values thanks to
guaranteed copy elision, when a chain of functions return different types, if the
programmer forgets to scatter std::move()
appropriately, copies rather than moves
tend to occur in non-obvious ways. No doubt future C++ standards will improve on the
automatic use of moves instead of copies where possible, but until then making all
your result
and outcome
types move-only is an excellent discipline.
Note that move-only result
and outcome
capable code (i.e. your project is in Experimental
Outcome configuration) usually compiles fine when result
and outcome
are copyable
(i.e. your project is in Standard Outcome configuration), albeit sometimes with a few
compiler warnings about unnecessary use of std::move()
.