v2.2 major changes
Major changes in v2.2 over v2.1 are listed here.
A new trait
is_move_bitcopying<T>
is added, which opts types into a library-based emulation of P1029 move = bitcopies. Experimentalstd::error
is opted in by default. If this trait is true for yourT
orE
type, Outcome will track moved-from status for your type, and will only call your type’s destructor if it was not moved from. If your compiler’s optimiser is sufficiently able to fold code, this improves codegen quality for Experimental Outcome very considerably, approaching the same gains as P1029 types would have. Note that the empirical performance difference will likely be nil, but the codegen does look much more elegant.If for
basic_result<T, E>
bothT
andE
are trivially copyable, union-based rather than struct-based storage will be used. This significantly improves performance in synthetic benchmarks which do nothing in deep call stacks of function calls except create and returnresult<T, E>
, and makes Outcome return competitive results to alternative error handling choices, improving comparative optics. It is not expected that the performance difference will be detectable empirically in real world code. It is expected that the build time impact of union storage won’t be noticeable, as union storage for trivially copyable types is much easier than for non-TC types.Note that storage remains struct-based if either
T
orE
is neither trivially copyable nor for which traitis_move_bitcopying<T>
is true. This is because union-based storage for complex types has significant build time impact, as anyone who has deployedstd::variant
orstd::expected
into globally visible public APIs will have noticed.The compile time requirement for
E
types to have a default constructor is removed.OUTCOME_TRY(var, expr)
no longer always declaresvar
asauto &&var
, but simply uses it as is. This allowsTRY
to initialise or assign. You can use the macroOUTCOME21_TRY
if you want the pre-Outcome v2.2 behaviour. You may find the regular expression_TRY\(([^(]*?),(.*?)\);
=>_TRY(auto &&\1,\2);
of use to you when upgrading code.OUTCOME_TRY
now declares its internal uniquely named temporary variable which holds the result of the expression asauto unique = expr
instead ofauto &&unique = expr
. This will cause TRY ofresult<UncopyableAndImmovable>
andoutcome<UncopyableAndImmovable>
to fail to compile, whereas previously they did compile. Another big change in semantic is that TRY now will ‘consume’ values moved into it, whereas previously it did not/ The reason for this change was that the previous behaviour produced undefined behaviour in various corner use cases, particulary in generic code. You can tell TRY to use references instead of values for its uniquely named temporary using a special syntax.OUTCOME_TRY
now propagates the value fromspare_storage(const basic_result|basic_outcome *) noexcept
of the input Result/Outcome into anyfailure_type<T>
returned by TRY. Result/Outcome now sets its spare storage value from anysuccess_type<T>
orfailure_type<T>
from which it is constructed. This is a breaking change, as spare storage values were not propagated beforehand. However this change means that any stack backtrace identifier captured by a failed result construction hook is now fully propagated from failure point up through all TRY operations to the code which handles the failure.The ADL discovered event hooks have been replaced with policy-specified event hooks instead. This is due to brittleness (where hooks would quietly self-disable if somebody changed something), compiler bugs (a difference in compiler settings causes the wrong hooks, or some but not all hooks, to get discovered), and end user difficulty in using them at all. The policy-specified event hooks can be told to default to ADL discovered hooks for backwards compatibility: set
OUTCOME_ENABLE_LEGACY_SUPPORT_FOR
to less than220
to enable emulation.