v2.2.9 ? (Boost 1.85) [release]


#293 - Some users wished that Outcome would be clean with -Wpedantic, this is now turned on for the test suite.

#294 - All use of assert() has been replaced with OUTCOME_ASSERT, which can be user overridden at compile time.

#295 - In git commit 12b14e1533848e9a0f7f3c38e41da0ee4e819770 (Aug 11 2022) status code had its paths changed due to its headers not previously having the right path convention. It was not realised at the time that in Boost.Outcome this resulted in <boost/outcome/experimental/status-code/status-code/headers.hpp> which is not desirable. This has now been remedied to remove the double status-code, which will obviously break any Boost.Outcome code which relies on the double status-code. Standalone Outcome is unaffected.

Bug fixes:

v2.2.8 13th December 2023 (Boost 1.84) [release]


Bug fixes:

#291 - A Result or Outcome with void value type and move-only non-value type was only usable in const use cases, due to the lack of provision of non-const member functions in relevant observers injection layers for the void specialisation. The missing non-const member functions have now been added.

v2.2.7 13th August 2023 (Boost 1.83) [release]


Bug fixes:

#273 - Changes to other Boost libraries had caused Boost.Outcome’s test suite to fail to compile for some compiler and C++ language configurations in recent releases. Thanks to work contributed by @alandefreitas and @pdimov, Boost.Outcome now CI tests a wide range of compilers and configurations and it is believed all those corner case issues have been fixed or worked around, for the compilers and configurations within that CI matrix.

Standalone Outcome’s test suite was never affected, as it did not have Boost changing underneath it. Nevertheless, a few of the compiler parse bug workarounds will have improved compatibility there too for atyical toolchain choices.

v2.2.6 24th March 2023 (Boost 1.82) [release]


Bug fixes:

v2.2.4 11th August 2022 (Boost 1.80) [release]


Bug fixes:

Move assignable with move constructible not available did not work with void.

v2.2.3 17th March 2022 (Boost 1.79) [release]


Standalone Outcome permanently locks ABI to v2.2.3 release
From this release onwards, you are guaranteed that any code compiled with v2.2.3 Outcome or newer will link without issue with any code compiled with a different version of Outcome after v2.2.3. As a result, OUTCOME_UNSTABLE_VERSION is no longer defined by default, and therefore OUTCOME_V2_NAMESPACE is now hard coded to outcome_v2 rather than a namespace which permutes per commit.

Bug fixes:

Fix enabling of move assignable was not calculated correctly.

v2.2.2 8th December 2021 (Boost 1.78) [release]


Restore Experimental Outcome constexpr compatibility in C++ 20 which was an undocumented property of the Outcome v2.1 series, and which had been dropped in the v2.2 series.
GCC Coroutines support
Coroutine support in GCCs after 10 is now correctly detected.

Bug fixes:

v2.2.1 13th August 2021 (Boost 1.77) [release]

Bug fixes:


Fix failure to compile Boost.Outcome when the compiler declares support for C++ modules.

Don’t use [[likely]] in GCCs before 9.


Make support for C++ modules opt-in.

v2.2.0 16th April 2021 (Boost 1.76) [release]

BREAKING CHANGE As announced for a year and three Boost releases, Outcome v2.2 became the default, replacing v2.1.
All v2.1 Outcome code will need to be upgraded as described in the v2.1 => v2.2 upgrade guide. This branch has a number of major breaking changes to Outcome v2.1, see the list of v2.2 major changes.


VS2019.8 compatibility
VS2019.8 changed how to enable Coroutines, which caused Outcome to not compile on that compiler.
If on C++ 20, we now use C++ 20 [[likely]] instead of compiler-specific markup to indicate when TRY has likely success or failure.

Previously the value of spare_storage(const basic_result|basic_outcome *) noexcept was not propagated over OUTCOME_TRY, which causes things like stack backtraces captured at the point of construction of an errored result to get dropped at every TRY point. This has been fixed by adding an optional spare_storage to success_type<T> and failure_type<T> , as well as to auto success(T &&, ...) and auto failure(T &&, ...) .

You should not notice this in your code, except that where before spare storage values did not propagate through TRY, now they do, which is a breaking change.

Bug fixes:


It came as a shock to learn that OUTCOME_TRY had been broken since the inception of this library for certain corner case code:

outcome::result<Foo>    get_foo();
outcome::result<Foo>    filter1(outcome::result<Foo> &&);
outcome::result<Foo> && filter2(outcome::result<Foo> &&);

// This works fine, and always has
OUTCOME_TRY(auto v, filter1(get_foo()))

// This causes UB due to result<Foo> being destructed before move of value into v
OUTCOME_TRY(auto v, filter2(get_foo()))

Whilst reference passthrough filter functions are not common, they can turn up in highly generic code, where destruction before copy/move is not helpful.

The cause is that TRY used to work by binding the result of the expression to an auto &&unique, testing if that unique if successful or not, and if successful then moving from unique.value() into the user’s output variable. If the expression returned is a prvalue, the Result’s lifetime is extended by the bound reference to outside of the statement, and all is good. If the expression returned is an xvalue or lvalue, then the lifetime extension does not exceed that of the statement, and the Result is destructed after the semicolon succeeding the assignment to auto &&unique.

This bug has been fixed by TRY deducing the value category of its input expression as follows:

  • prvalues => auto unique = (expr) (breaking change)
  • xvalue => auto unique = (expr) (breaking change)
  • lvalue => auto unique = (expr) (breaking change)

This ensures that xvalue and lvalue inputs do not cause unhelpfully early lifetime end, though it does silently change the behaviour of existing code which relied on rvalues and lvalues being passed through, as a new construct-move-destruct or construct-copy-destruct cycle is introduced to where there was none before. Also, before C++ 17, there is now an added copy/move for prvalue inputs, which does not affect runtime codegen due to Return Value Optimisation (RVO), but does cause Results containing non-copying non-moving types to fail to compile, which is a breaking change from beforehand.

If one wishes rvalues or lvalues to be passed through, one can avail of a new TRY syntax based on preprocessor overloading:

  • OUTCOME_TRY((refspec, varname), expr)
  • OUTCOME_TRYV2(refspec, expr)

Here refspec is the storage to be used for both the internal temporary unique, AND varname. So if you write:

Foo &&foo;
OUTCOME_TRY((auto &&, v), filter2(foo))

… then the internal unique is declared as auto &&unique = (filter2(foo)), and the output variable is declared as auto &&v = std::move(unique).assume_value(). This passes through the rvalue referencing, and completely avoids copies and moves of Foo. If you wish to not extract the value but also specify unique storage, there is a new OUTCOME_TRYV2(refspec, expr).

My thanks to KamilCuk from for all their help in designing the new overloaded TRY syntax. My thanks also to vasama for reporting this issue and working through how best to fix it with me.


The preprocessor logic for choosing when to use bool with concept on GCC was yet again refactored. This should fix those choices of GCC configuration which caused failure due to the wrong combination being chosen.

v2.1.5 11th December 2020 (Boost 1.75) [release]


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 than 220 to enable emulation.
Improve configuring OUTCOME_GCC6_CONCEPT_BOOL
Older GCCs had boolean based concepts syntax, whereas newer GCCs are standards conforming. However the precise logic of when to use legacy and conforming syntax was not well understood, which caused Outcome to fail to compile depending on what options you pass to GCC. The new logic always uses the legacy syntax if on GCC 8 or older, otherwise we use conforming syntax if and only if GCC is in C++ 20 mode or later. This hopefully will resolve the corner case build failures on GCC.

Bug fixes:

Boost.Outcome should now compile with BOOST_NO_EXCEPTIONS defined
Thanks to Emil, maintainer of Boost.Exception, making a change for me, Boost.Outcome should now compile with C++ exceptions globally disabled. You won’t be able to use boost::exception_ptr as it can’t be included if C++ exceptions are globally disabled.
In the Coroutine support the final_suspend() was not noexcept, despite being required to be so in the C++ 20 standard. This has been fixed, but only if your compiler implements noop_coroutine. Additionally, if noop_coroutine is available, we use the much more efficient coroutine handle returning variant of await_suspend() which should significantly improve codegen and context switching performance.

v2.1.4 14th August 2020 (Boost 1.74) [release]


BREAKING CHANGE void results and outcomes no longer default construct types during explicit construction
Previously if you explicitly constructed a result<T> from a non-errored result<void>, it default constructed T. This was found to cause unhelpful surprise, so it has been disabled.
The macro OUTCOME_ENABLE_LEGACY_SUPPORT_FOR can be used to enable aliasing of older naming and features to newer naming and features when using a newer version of Outcome.
Concepts now have snake case style naming instead of camel case style
When Outcome was first implemented, it was thought that C++ 20 concepts were going to have camel case style. This was changed before the C++ 20 release, and Outcome’s concepts have been renamed similarly. This won’t break any code in Outcome v2.1, as compatibility aliases are provided. However code compiled against Outcome v2.2 will need to be upgraded, unless OUTCOME_ENABLE_LEGACY_SUPPORT_FOR is set to less than 220.
Concepts now live in OUTCOME_V2_NAMESPACE::concepts namespace
Previously concepts lived in the convert namespace, now they live in their own namespace.
New concepts basic_result<T> and basic_outcome<T> added
End users were finding an unhelpful gap in between is_basic_result<T> and value_or_error<T> where they wanted a concept that matched types which were basic_result, but not exactly one of those. Concepts filling that gap were added.
Operation TRY works differently from Outcome v2.2 onwards
This is a severely code breaking change which change the syntax of how one uses OUTCOME_TRY(). A regular expression suitable for upgrading code can be found in the list of changes between Outcome v2.1 and v2.2.

Bug fixes:

The clang Apple ships in Xcode 11.4 (currently the latest) has not been patched with the fixes to LLVM clang that fix noexcept(std::is_constructible<T, void>) failing to compile which I originally submitted years ago. So give up waiting on Apple to fix their clang, add a workaround to Outcome.
Use of void in T or E caused noexcept(false)
Direct traits examination of void was causing nothrow detection to return false, fixed.
Spare storage could not be used from within no-value policy classes
Due to an obvious brain fart when writing the code at the time, the spare storage APIs had the wrong prototype which prevented them working from within policy classes. Sorry.

v2.1.3 29th April 2020 (Boost 1.73) [release]


Performance of Outcome-based code compiled by clang has been greatly improved

The previous implementation of Outcome’s status bitfield confused clang’s optimiser, which caused low quality codegen. Unlike most codegen issues, this was noticeably in empirical benchmarks of real world code, as was shown by P1886 Error speed benchmarking.

The safe part of the better_optimisation Outcome v2.2.0 future branch was merged to Outcome v2.1.3 which includes a new status bitfield implementation. This appears to not confuse clang’s optimiser, and clang 9 produces code which routinely beats GCC 9’s code for various canned use cases.

Precompiled headers are automatically enabled on new enough cmake’s for standalone Outcome

If on cmake 3.16 or later, its new precompiled headers build support is used to tell consumers of the outcome::hl cmake target to precompile Outcome, if and only if outcome_IS_DEPENDENCY is false. outcome_IS_DEPENDENCY is set by Outcome’s CMakeLists.txt if it detects that it was included using add_subdirectory(), so for the vast majority of Outcome end users, the use of precompiled headers will NOT be enabled.

Exported targets do NOT request precompilation of headers, as it is assumed that importers of the Outcome cmake targets will configure their own precompiled headers which incorporate Outcome.

Installability is now CI tested per commit

Due to installability of standalone Outcome (e.g. make install) breaking itself rather more frequently than is ideal, installability is now tested on CI per commit.

Coroutines support has been documented

The coroutines support added in v2.1.2 has now been properly documented.

Bug fixes:

Newer Concepts implementing compilers were unhappy with the early check for destructibility of T and E, so removed template constraints, falling back to static assert which runs later in the type instantiation sequence.
For standalone Outcome, CMAKE_TOOLCHAIN_FILE is now passed through during dependency superbuild. This should solve build issues for some embedded toolchain users.
A false positive undefined behaviour sanitiser failure in some use cases of Experimental Outcome was worked around to avoid the failure message.
Restored compatibility with x86 on Windows, which was failing with link errors. It was quite surprising that this bug was not reported sooner, but obviously almost nobody is using Outcome with x86 on Windows.
Fix a segfault in Debug builds only when cloning a status_code_ptr in Experimental.Outcome only.

v2.1.2 11th December 2019 (Boost 1.72) [release]


Improved compatibility with cmake tooling
Standalone outcome is now make install-able, and cmake find_package() can find it. Note that you must separately install and find_package() Outcome’s dependency, quickcpplib, else find_package() of Outcome will fail.
Non-permissive parsing is now default in Visual Studio
The default targets in standalone Outcome’s cmake now enable non-permissive parsing. This was required partially because VS2019 16.3’s quite buggy Concepts implementation is unusuable in permissive parsing mode. Even then, lazy ADL two phase lookup is broken in VS2019 16.3 with /std:latest, you may wish to use an earlier language standard.
Breaking change!
The git submodule mechanism used by standalone Outcome of specifying dependent libraries has been replaced with a cmake superbuild of dependencies mechanism instead. Upon cmake configure, an internal copy of quickcpplib will be git cloned, built and installed into the build directory from where an internal find_package() uses it. This breaks the use of the unconfigured Outcome repo as an implementation of Outcome, one must now do one of:
  1. Add Outcome as subdirectory to cmake build.
  2. Use cmake superbuild (i.e. ExternalProject_Add()) to build and install Outcome into a local installation.
  3. Use one of the single header editions.
Breaking change!
For standalone Outcome, the current compiler is now checked for whether it will compile code containing C++ Concepts, and if it does, all cmake consumers of Outcome will enable C++ Concepts. Set the cmake variable CXX_CONCEPTS_FLAGS to an empty string to prevent auto detection and enabling of C++ Concepts support occurring.
OUTCOME_TRY operation now hints to the compiler that operation will be successful
P1886 Error speed benchmarking showed that there is considerable gain in very small functions by hinting to the compiler whether the expression is expected to be successful or not. OUTCOME_TRY previously did not hint to the compiler at all, but now it does. A new suite of macros OUTCOME_TRY_FAILURE_LIKELY hint to the compiler that failure is expected. If you wish to return to the previously unhinted behaviour, define OUTCOME_TRY_LIKELY(expr) to (!!expr).
Support for C++ Coroutines has been added. This comes in two parts, firstly there is now an OUTCOME_CO_TRY() operation suitable for performing the TRY operation from within a C++ Coroutine. Secondly, in the header outcome/coroutine_support.hpp there are implementations of eager<OutcomeType> and lazy<OutcomeType> which let you more naturally and efficiently use basic_result or basic_outcome from within C++ Coroutines – specifically, if the result or outcome will construct from an exception pointer, exceptions thrown in the coroutine return an errored or excepted result with the thrown exception instead of throwing the exception through the coroutine machinery (which in current compilers, has a high likelihood of blowing up the program). Both eager<T> and lazy<T> can accept any T as well. Both have been tested and found working on VS2019 and clang 9.
make_error_code() and make_exception_ptr() are now additionally considered for compatible copy and move conversions for basic_result<>. This lets you construct a basic_result<T, E> into a basic_result<T, error_code>, where E is a custom type which has implemented the ADL discovered free function error_code make_error_code(E), but is otherwise unrelated to error_code. The same availability applies for exception_ptr with make_exception_ptr() being the ADL discovered free function. basic_outcome<> has less support for this than basic_result<> in order to keep constructor count down, but it will accept via this mechanism conversions from basic_result<> and failure_type<>.

Bug fixes:

The detection of [[nodiscard]] support in the compiler was very mildly broken.

v2.1.1 19th August 2019 (Boost 1.71) [release]


As per request from Boost release managers, relocated version.hpp and revision.hpp into detail, and added the Boost licence boilerplate to the top of every source file which was missing one (I think). Also took the opportunity to run the licence restamping script over all Outcome, so copyright dates are now up to date.
Add FAQ item explaining issue #185, and why we will do nothing to fix it right now.
Refactored the OUTCOME_TRY implementation to use more clarified customisation points capable of accepting very foreign inputs. Removed the std::experimental::expected<T, E> specialisations, as those are no longer necessary. Fixed the documentation for the customisation points which previously claimed that they are ADL discovered, which they are not. Added a recipe describing how to add in support for foreign input types.
Added a separate motivation/plug_error_code specifically for Boost.

Bug fixes:

OUTCOME_VERSION_MINOR hadn’t been updated to 1.
Fix issue #181 where Outcome didn’t actually implement the strong swap guarantee, despite being documented as doing so.
Fix issue #190 in Boost edition where unit test suite was not runnable from the Boost release distro.
Fix issue #182 where trait::is_exception_ptr_available<T> was always true, thus causing much weirdness, like not printing diagnostics and trying to feed everything to make_exception_ptr().
Fix issue #192 where the std::basic_outcome_failure_exception_from_error() was being defined twice for translation units which combine standalone and Boost Outcome’s.

v2.1 12th Apr 2019 (Boost 1.70) [release]

v2.0 18th Jan 2018 [release]