1<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> 2<html><meta http-equiv="Content-Type" content="text/html; charset=utf-8"> 3<title>Frequently asked questions - Boost.Outcome documentation</title> 4<link rel="stylesheet" href="./css/boost.css" type="text/css"> 5<meta name="generator" content="Hugo 0.52 with Boostdoc theme"> 6<meta name="viewport" content="width=device-width,initial-scale=1.0"/> 7 8<link rel="icon" href="./images/favicon.ico" type="image/ico"/> 9<body><div class="spirit-nav"> 10<a accesskey="p" href="./reference/functions/try_throw_std_exception_from_error.html"><img src="./images/prev.png" alt="Prev"></a> 11 <a accesskey="u" href="./index.html"><img src="./images/up.png" alt="Up"></a> 12 <a accesskey="h" href="./index.html"><img src="./images/home.png" alt="Home"></a><a accesskey="n" href="./videos.html"><img src="./images/next.png" alt="Next"></a></div><div id="content"> 13 14 <div class="titlepage"><div><div><h1 style="clear: both">Frequently asked questions</h1></div></div></div> 15 <div class="toc"><dl class="toc"> 16<dt> 17<dd><dl> 18<dt><a href="#is-outcome-safe-to-use-in-extern-apis">Is Outcome safe to use in extern APIs?</a></dt> 19<dt><a href="#does-outcome-implement-over-alignment">Does Outcome implement over-alignment?</a></dt> 20<dt><a href="#does-outcome-implement-the-no-fail-strong-or-basic-exception-guarantee">Does Outcome implement the no-fail, strong or basic exception guarantee?</a></dt> 21<dt><a href="#does-outcome-have-a-stable-abi-and-api">Does Outcome have a stable ABI and API?</a></dt> 22<dt><a href="#can-i-use-result-t-ec-across-dll-shared-object-boundaries">Can I use <code>result<T, EC></code> across DLL/shared object boundaries?</a></dt> 23<dt><a href="#why-two-types-result-and-outcome-rather-than-just-one">Why two types <code>result<></code> and <code>outcome<></code>, rather than just one?</a></dt> 24<dt><a href="#how-badly-will-including-outcome-in-my-public-interface-affect-compile-times">How badly will including Outcome in my public interface affect compile times?</a></dt> 25<dt><a href="#is-outcome-suitable-for-fixed-latency-predictable-execution-coding-such-as-for-high-frequency-trading-or-audio">Is Outcome suitable for fixed latency/predictable execution coding such as for high frequency trading or audio?</a></dt> 26<dt><a href="#what-kind-of-runtime-performance-impact-will-using-outcome-in-my-code-introduce">What kind of runtime performance impact will using Outcome in my code introduce?</a> 27<dd><dl> 28<dt><a href="#high-end-cpu-intel-skylake-x64">High end CPU: Intel Skylake x64</a></dt> 29<dt><a href="#mid-tier-cpu-arm-cortex-a72">Mid tier CPU: ARM Cortex A72</a></dt> 30<dt><a href="#low-end-cpus-intel-silvermont-x64-and-arm-cortex-a53">Low end CPUs: Intel Silvermont x64 and ARM Cortex A53</a></dt> 31</dl></dd></dt> 32<dt><a href="#why-is-implicit-default-construction-disabled">Why is implicit default construction disabled?</a></dt> 33<dt><a href="#how-far-away-from-the-proposed-std-expected-t-e-is-outcome-s-checked-t-e">How far away from the proposed <code>std::expected<T, E></code> is Outcome’s <code>checked<T, E></code>?</a></dt> 34<dt><a href="#why-doesn-t-outcome-duplicate-std-expected-t-e-s-design">Why doesn’t Outcome duplicate <code>std::expected<T, E></code>’s design?</a></dt> 35<dt><a href="#is-outcome-riddled-with-undefined-behaviour-for-const-const-containing-and-reference-containing-types">Is Outcome riddled with undefined behaviour for const, const-containing and reference-containing types?</a> 36<dd><dl> 37<dt><a href="#more-detail">More detail</a></dt> 38</dl></dd></dt> 39</dl></dd></dt> 40</dl> 41 </div> 42 43 44 45 46<h2 id="is-outcome-safe-to-use-in-extern-apis">Is Outcome safe to use in extern APIs?</h2> 47 48<p>Outcome is specifically designed for use in the public interfaces of multi-million 49line codebases. <code>result</code>’s layout is hard coded to:</p> 50<div class="highlight"><pre class="chroma"><code class="language-c" data-lang="c"><span class="k">struct</span> 51<span class="p">{</span> 52 <span class="n">T</span> <span class="n">value</span><span class="p">;</span> 53 <span class="kt">unsigned</span> <span class="kt">int</span> <span class="n">flags</span><span class="p">;</span> 54 <span class="n">EC</span> <span class="n">error</span><span class="p">;</span> 55<span class="p">};</span></code></pre></div> 56<p>This is C-compatible if <code>T</code> and <code>EC</code> are C-compatible. 57<a href="https://en.cppreference.com/w/cpp/error/error_code" class="api-reference" target="_blank"><i class="fa fa-book" aria-hidden="true"></i> <code>std::error_code</code></a> 58 59is <em>probably</em> C-compatible, but its layout is not standardised (though there is a 60normative note in the standard about its layout). Hence Outcome cannot provide a 61C macro API for standard Outcome, but we can for <a href="./experimental/c-api.html">Experimental Outcome</a>.</p> 62 63<h2 id="does-outcome-implement-over-alignment">Does Outcome implement over-alignment?</h2> 64 65<p>Variant-based alternatives to Outcome such as 66<a href="https://wg21.link/P0323" class="api-reference" target="_blank"><i class="fa fa-book" aria-hidden="true"></i> P0323 <code>std::expected<T, E></code></a> 67 68would use <code>std::aligned_union</code> to ensure appropriate over-alignment for the storage of 69either a <code>T</code> or an <code>E</code>. This discovers the over-alignment for a type using 70<code>std::alignment_of</code>, which is defaulted to <code>alignof()</code>.</p> 71 72<p>Outcome uses <code>struct</code>-based storage, as described above. Any over-alignment of 73<code>result</code> or <code>outcome</code> will follow the ordinary alignment and padding rules for 74<code>struct</code> on your compiler. Traits such as <code>std::alignment_of</code>, or other standard 75library facilities, are not used.</p> 76 77<h2 id="does-outcome-implement-the-no-fail-strong-or-basic-exception-guarantee">Does Outcome implement the no-fail, strong or basic exception guarantee?</h2> 78 79<p>(<a href="https://en.cppreference.com/w/cpp/language/exceptions#Exception_safety">You can read about the meaning of these guarantees at cppreference.com</a>)</p> 80 81<p>If for the following operations:</p> 82 83<ul> 84<li>Construction</li> 85<li>Assignment</li> 86<li>Swap</li> 87</ul> 88 89<p>… the corresponding operation in <strong>all</strong> of <code>value_type</code>, <code>error_type</code> (and 90<code>exception_type</code> for <code>outcome</code>) is <code>noexcept(true)</code>, then <code>result</code> and 91<code>outcome</code>’s operation is <code>noexcept(true)</code>. This propagates the no-fail exception 92guarantee of the underlying types. Otherwise the basic guarantee applies for all 93but Swap, under the same rules as for the <code>struct</code> layout type given above e.g. 94value would be constructed first, then the flags, then the error. If the error 95throws, value and status bits would be as if the failure had not occurred, same 96as for aborting the construction of any <code>struct</code> type.</p> 97 98<p>It is recognised that these weak guarantees may be unsuitable for some people, 99so Outcome implements <code>swap()</code> with much stronger guarantees, as one can locally refine, 100without too much work, one’s own custom classes from <code>result</code> and <code>outcome</code> implementing 101stronger guarantees for construction and assignment using <code>swap()</code> as the primitive 102building block.</p> 103 104<p>The core ADL discovered implementation of strong guarantee swap is <a href="./reference/functions/strong_swap.html" class="api-reference"><code>strong_swap(bool &all_good, T &a, T &b)</code></a> 105. 106This can be overloaded by third party code with custom strong guarantee swap 107implementations, same as for <code>std::swap()</code>. Because strong guarantee swap may fail 108when trying to restore input state during handling of failure to swap, the 109<code>all_good</code> boolean becomes false if restoration fails, at which point both 110results/outcomes get marked as tainted via <a href="./reference/types/basic_result/has_lost_consistency.html" class="api-reference"><code>has_lost_consistency()</code></a> 111.</p> 112 113<p>It is <strong>up to you</strong> to check this flag to see if known good state has been lost, 114as Outcome never does so on your behalf. The simple solution to avoiding having 115to deal with this situation is to always choose your value, error and exception 116types to have non-throwing move constructors and move assignments. This causes 117the strong swap implementation to no longer be used, as it is no longer required, 118and standard swap is used instead.</p> 119 120<h2 id="does-outcome-have-a-stable-abi-and-api">Does Outcome have a stable ABI and API?</h2> 121 122<p>Right now, no. Though the data layout shown above is not expected to change.</p> 123 124<p>Outcome’s ABI and API will be formally fixed as <strong>the</strong> v2 interface approximately 125one year after its first Boost release. Thereafter the 126<a href="https://lvc.github.io/abi-compliance-checker/">ABI compliance checker</a> 127will be run per-commit to ensure Outcome’s ABI and API remains stable.</p> 128 129<p>Note that the stable ABI and API guarantee will only apply to standalone 130Outcome, not to Boost.Outcome. Boost.Outcome has dependencies on other 131parts of Boost which are not stable across releases.</p> 132 133<p>Note also that the types you configure a <code>result</code> or <code>outcome</code> with also need 134to be ABI stable if <code>result</code> or <code>outcome</code> is to be ABI stable.</p> 135 136<h2 id="can-i-use-result-t-ec-across-dll-shared-object-boundaries">Can I use <code>result<T, EC></code> across DLL/shared object boundaries?</h2> 137 138<p>A known problem with using DLLs (and to smaller extent shared libraries) is that global 139objects may get duplicated: one instance in the executable and one in the DLL. This 140behaviour is not incorrect according to the C++ Standard, as the Standard does not 141recognize the existence of DLLs or shared libraries. Therefore, program designs that 142depend on globals having unique addresses may become compromised when used in a program 143using DLLs.</p> 144 145<p>Nothing in Outcome depends on the addresses of globals, plus the guaranteed fixed data 146layout (see answer above) means that different versions of Outcome can be used in 147different DLLs, and it probably will work okay (it is still not advised that you do that 148as that is an ODR violation). 149However, one of the most likely candidate for <code>EC</code> – <code>std::error_code</code> – does depend 150on the addresses of globals for correct functioning.</p> 151 152<p>The standard library is required to implement globally unique addresses for the standard library 153provided 154<a href="https://en.cppreference.com/w/cpp/error/error_category" class="api-reference" target="_blank"><i class="fa fa-book" aria-hidden="true"></i> <code>std::error_category</code></a> 155 implementations e.g. <code>std::system_category()</code>. 156User defined error code categories may <strong>not</strong> have unique global addresses, and thus 157introduce misoperation.</p> 158 159<p><code>boost::system::error_code</code>, since version 1.69 does offer an <em>opt-in</em> guarantee 160that it does not depend on the addresses of globals <strong>if</strong> the user defined error code 161category <em>opts-in</em> to the 64-bit comparison mechanism. This can be seen in the specification of 162<code>error_category::operator==</code> in 163<a href="https://www.boost.org/doc/libs/1_69_0/libs/system/doc/html/system.html#ref_synopsis">Boost.System synopsis</a>.</p> 164 165<p>Alternatively, the <code>status_code</code> in <a href="(/experimental/differences.html)">Experimental Outcome</a>, 166due to its more modern design, does not suffer from any problems from being used in shared 167libraries in any configuration.</p> 168 169<h2 id="why-two-types-result-and-outcome-rather-than-just-one">Why two types <code>result<></code> and <code>outcome<></code>, rather than just one?</h2> 170 171<p><code>result</code> is the simple, success OR failure type.</p> 172 173<p><code>outcome</code> extends <code>result</code> with a third state to transport, conventionally (but not necessarily) some sort of “abort” or “exceptional” state which a function can return to indicate that not only did the operation fail, but it did so <em>catastrophically</em> i.e. please abort any attempt to retry the operation.</p> 174 175<p>A perfect alternative to using <code>outcome</code> is to throw a C++ exception for the abort code path, and indeed most programs ought to do exactly that instead of using <code>outcome</code>. However there are a number of use cases where choosing <code>outcome</code> shines:</p> 176 177<ol> 178<li>Where C++ exceptions or RTTI is not available, but the ability to fail catastrophically without terminating the program is important.</li> 179<li>Where deterministic behaviour is required even in the catastrophic failure situation.</li> 180<li>In unit test suites of code using Outcome it is extremely convenient to accumulate test failures into an <code>outcome</code> for later reporting. A similar convenience applies to RPC situations, where C++ exception throws need to be accumulated for reporting back to the initiating endpoint.</li> 181<li>Where a function is “dual use deterministic” i.e. it can be used deterministically, in which case one switches control flow based on <code>.error()</code>, or it can be used non-deterministically by throwing an exception perhaps carrying a custom payload.</li> 182</ol> 183 184<h2 id="how-badly-will-including-outcome-in-my-public-interface-affect-compile-times">How badly will including Outcome in my public interface affect compile times?</h2> 185 186<p>The quick answer is that it depends on how much convenience you want.</p> 187 188<p>The convenience header <code><result.hpp></code> is dependent on <code><system_error></code> or Boost.System, which unfortunately includes <code><string></code> and thus 189drags in quite a lot of other slow-to-parse stuff. If your public interface already includes <code><string></code>, 190then the impact of additionally including Outcome will be low. If you do not include <code><string></code>, 191unfortunately impact may be relatively quite high, depending on the total impact of your 192public interface files.</p> 193 194<p>If you’ve been extremely careful to avoid ever including the most of the STL headers 195into your interfaces in order to maximise build performance, then <code><basic_result.hpp></code> 196can have as few dependencies as:</p> 197 198<ol> 199<li><code><cstdint></code></li> 200<li><code><initializer_list></code></li> 201<li><code><iosfwd></code></li> 202<li><code><new></code></li> 203<li><code><type_traits></code></li> 204<li><code><cstdio></code></li> 205<li><code><cstdlib></code></li> 206<li><code><cassert></code></li> 207</ol> 208 209<p>These, apart from <code><iosfwd></code>, tend to be very low build time impact in most standard 210library implementations. If you include only <code><basic_result.hpp></code>, and manually configure 211<code>basic_result<></code> by hand, compile time impact will be minimised.</p> 212 213<p>(See reference documentation for <a href="./reference/types/basic_result.html" class="api-reference"><code>basic_result<T, E, NoValuePolicy></code></a> 214 for more detail.</p> 215 216<h2 id="is-outcome-suitable-for-fixed-latency-predictable-execution-coding-such-as-for-high-frequency-trading-or-audio">Is Outcome suitable for fixed latency/predictable execution coding such as for high frequency trading or audio?</h2> 217 218<p>Great care has been taken to ensure that Outcome never unexpectedly executes anything 219with unbounded execution times such as <code>malloc()</code>, <code>dynamic_cast<>()</code> or <code>throw</code>. 220Outcome works perfectly with C++ exceptions and RTTI globally disabled.</p> 221 222<p>Outcome’s entire design premise is that its users are happy to exchange a small, predictable constant overhead 223during successful code paths, in exchange for predictable failure code paths.</p> 224 225<p>In contrast, table-based exception handling gives zero run time overhead for the 226successful code path, and completely unpredictable (and very expensive) overhead 227for failure code paths.</p> 228 229<p>For code where predictability of execution, no matter the code path, is paramount, 230writing all your code to use Outcome is not a bad place to start. Obviously enough, 231do choose a non-throwing policy when configuring <code>outcome</code> or <code>result</code> such as 232<a href="./reference/policies/all_narrow.html" class="api-reference"><code>all_narrow</code></a> 233 to guarantee that exceptions can never be thrown by Outcome 234(or use the convenience typedef for <code>result</code>, <a href="./reference/aliases/unchecked.html" class="api-reference"><code>unchecked<T, E = varies></code></a> 235 which uses <code>policy::all_narrow</code>).</p> 236 237<h2 id="what-kind-of-runtime-performance-impact-will-using-outcome-in-my-code-introduce">What kind of runtime performance impact will using Outcome in my code introduce?</h2> 238 239<p>It is very hard to say anything definitive about performance impacts in codebases one 240has never seen. Each codebase is unique. However to come up with some form of measure, 241we timed traversing ten stack frames via each of the main mechanisms, including the 242“do nothing” (null) case.</p> 243 244<p>A stack frame is defined to be something called by the compiler whilst 245unwinding the stack between the point of return in the ultimate callee and the base 246caller, so for example ten stack allocated objects might be destructed, or ten levels 247of stack depth might be unwound. This is not a particularly realistic test, but it 248should at least give one an idea of the performance impact of returning Outcome’s 249<code>result</code> or <code>outcome</code> over say returning a plain integer, or throwing an exception.</p> 250 251<p>The following figures are for Outcome v2.1.0 with GCC 7.4, clang 8.0 and Visual 252Studio 2017.9. Figures for newer Outcomes with newer compilers can be found at 253<a href="https://github.com/ned14/outcome/tree/develop/benchmark">https://github.com/ned14/outcome/tree/develop/benchmark</a>.</p> 254 255<h3 id="high-end-cpu-intel-skylake-x64">High end CPU: Intel Skylake x64</h3> 256 257<p>This is a high end CPU with very significant ability to cache, predict, parallelise 258and execute out-of-order such that tight, repeated loops perform very well. It has 259a large μop cache able to wholly contain the test loop, meaning that these results 260are a <strong>best case</strong> performance.</p> 261 262<figure> 263 <img src="./faq/results_skylake_log.png"/> <figcaption> 264 <h4>Log graph comparing GCC 7.4, clang 8.0 and Visual Studio 2017.9 on x64, for exceptions-globally-disabled, ordinary and link-time-optimised build configurations.</h4> 265 </figcaption> 266</figure> 267 268<p>As you can see, throwing and catching an exception is 269expensive on table-based exception handling implementations such as these, anywhere 270between 26,000 and 43,000 CPU cycles. And this is the <em>hot path</em> situation, this 271benchmark is a loop around hot cached code. If the tables are paged out onto storage, 272you are talking about <strong>millions</strong> of CPU cycles.</p> 273 274<p>Simple integer returns (i.e. do nothing null case) 275are always going to be the fastest as they do the least work, and that costs 80 to 90 276CPU cycles on this Intel Skylake CPU.</p> 277 278<p>Note that returning a <code>result<int, std::error_code></code> with a “success (error code)” 279is no more than 5% added runtime overhead over returning a naked int on GCC and clang. On MSVC 280it costs an extra 20% or so, mainly due to poor code optimisation in the VS2017.9 compiler. Note that “success 281(experimental status code)” optimises much better, and has almost no overhead over a 282naked int.</p> 283 284<p>Returning a <code>result<int, std::error_code></code> with a “failure (error code)” 285is less than 5% runtime overhead over returning a success on GCC, clang and MSVC.</p> 286 287<p>You might wonder what happens if type <code>E</code> has a non-trivial destructor, thus making the 288<code>result<T, E></code> have a non-trivial destructor? We tested <code>E = std::exception_ptr</code> and 289found less than a 5% overhead to <code>E = std::error_code</code> for returning success. Returning a failure 290was obviously much slower at anywhere between 300 and 1,100 CPU cycles, due to the 291dynamic memory allocation and free of the exception ptr, plus at least two atomic operations per stack frame, but that is 292still two orders of magnitude better than throwing and catching an exception.</p> 293 294<p>We conclude that if failure is anything but extremely rare in your C++ codebase, 295using Outcome instead of throwing and catching exceptions ought to be quicker overall:</p> 296 297<ul> 298<li>Experimental Outcome is statistically indistinguishable from the null case on this 299high end CPU, for both returning success and failure, on all compilers.</li> 300<li>Standard Outcome is less than 5% 301worse than the null case for returning successes on GCC and clang, and less than 10% worse than 302the null case for returning failures on GCC and clang.</li> 303<li>Standard Outcome optimises 304poorly on VS2017.9, indeed markedly worse than on previous point releases, so let’s 305hope that Microsoft fix that soon. It currently has a less than 20% overhead on the null case.</li> 306</ul> 307 308<h3 id="mid-tier-cpu-arm-cortex-a72">Mid tier CPU: ARM Cortex A72</h3> 309 310<p>This is a four year old mid tier CPU used in many high end mobile phones and tablets 311of its day, with good ability to cache, predict, parallelise 312and execute out-of-order such that tight, repeated loops perform very well. It has 313a μop cache able to wholly contain the test loop, meaning that these results 314are a <strong>best case</strong> performance.</p> 315 316<figure> 317 <img src="./faq/results_arm_a72_log.png"/> <figcaption> 318 <h4>Log graph comparing GCC 7.3 and clang 7.3 on ARM64, for exceptions-globally-disabled, ordinary and link-time-optimised build configurations.</h4> 319 </figcaption> 320</figure> 321 322<p>This ARM chip is a very consistent performer – null case, success, or failure, all take 323almost exactly the same CPU cycles. Choosing Outcome, in any configuration, makes no 324difference to not using Outcome at all. Throwing and catching a C++ exception costs 325about 90,000 CPU cycles, whereas the null case/Outcome costs about 130 - 140 CPU cycles.</p> 326 327<p>There is very little to say about this CPU, other than Outcome is zero overhead on it. The same 328applied to the ARM Cortex A15 incidentally, which I test cased extensively when 329deciding on the Outcome v2 design back after the first peer review. The v2 design 330was chosen partially because of such consistent performance on ARM.</p> 331 332<h3 id="low-end-cpus-intel-silvermont-x64-and-arm-cortex-a53">Low end CPUs: Intel Silvermont x64 and ARM Cortex A53</h3> 333 334<p>These are low end CPUs with a mostly or wholly in-order execution core. They have a small 335or no μop cache, meaning that the CPU must always decode the instruction stream. 336These results represent an execution environment more typical of CPUs two decades 337ago, back when table-based EH created a big performance win if you never threw 338an exception.</p> 339 340<p><figure> 341 <img src="./faq/results_silvermont_log.png"/> <figcaption> 342 <h4>Log graph comparing GCC 7.3 and clang 7.3 on x64, for exceptions-globally-disabled, ordinary and link-time-optimised build configurations.</h4> 343 </figcaption> 344</figure> 345<figure> 346 <img src="./faq/results_arm_a53_log.png"/> <figcaption> 347 <h4>Log graph comparing GCC 7.3 and clang 7.3 on ARM64, for exceptions-globally-disabled, ordinary and link-time-optimised build configurations.</h4> 348 </figcaption> 349</figure></p> 350 351<p>The first thing to mention is that clang generates very high performance code for 352in-order cores, far better than GCC. It is said that this is due to a very large investment by 353Apple in clang/LLVM for their devices sustained over many years. In any case, if you’re 354targeting in-order CPUs, don’t use GCC if you can use clang instead!</p> 355 356<p>For the null case, Silvermont and Cortex A53 are quite similar in terms of CPU clock cycles. Ditto 357for throwing and catching a C++ exception (approx 150,000 CPU cycles). However the Cortex 358A53 does far better with Outcome than Silvermont, a 15% versus 100% overhead for Standard 359Outcome, and a 4% versus 20% overhead for Experimental Outcome.</p> 360 361<p>Much of this large difference is in fact due to calling convention differences. x64 permits up to 8 bytes 362to be returned from functions by CPU register. <code>result<int></code> consumes 24 bytes, so on x64 363the compiler writes the return value to the stack. However ARM64 permits up to 64 bytes 364to be returned in registers, so <code>result<int></code> is returned via CPU registers on ARM64.</p> 365 366<p>On higher end CPUs, memory is read and written in cache lines (32 or 64 bytes), and 367reads and writes are coalesced and batched together by the out-of-order execution core. On these 368low end CPUs, memory is read and written sequentially per assembler instruction, 369so only one load or one store to L1 370cache can occur at a time. This makes writing the stack particularly slow on in-order 371CPUs. Memory operations which “disappear” on higher end CPUs take considerable time 372on low end CPUs. This particularly punishes Silvermont in a way which does not punish 373the Cortex A53, because of having to write multiple values to the stack to create the 37424 byte object to be returned.</p> 375 376<p>The conclusion to take away from this is that if you are targeting a low end CPU, 377table-based EH still delivers significant performance improvements for the success 378code path. Unless determinism in failure is critically important, you should not 379use Outcome on in-order execution CPUs.</p> 380 381<h2 id="why-is-implicit-default-construction-disabled">Why is implicit default construction disabled?</h2> 382 383<p>This was one of the more interesting points of discussion during the peer review of 384Outcome v1. v1 had a formal empty state. This came with many advantages, but it 385was not felt to be STL idiomatic as <code>std::optional<result<T>></code> is what was meant, so 386v2 has eliminated any legal possibility of being empty.</p> 387 388<p>The <code>expected<T, E></code> proposal of that time (May 2017) did permit default construction 389if its <code>T</code> type allowed default construction. This was specifically done to make 390<code>expected<T, E></code> more useful in STL containers as one can say resize a vector without 391having to supply an <code>expected<T, E></code> instance to fill the new items with. However 392there was some unease with that design choice, because it may cause programmers to 393use some type <code>T</code> whose default constructed state is overloaded with additional meaning, 394typically “to be filled” i.e. a de facto empty state via choosing a magic value.</p> 395 396<p>For the v2 redesign, the various arguments during the v1 review were considered. 397Unlike <code>expected<T, E></code> which is intended to be a general purpose Either monad 398vocabulary type, Outcome’s types are meant primarily for returning success or failure 399from functions. The API should therefore encourage the programmer to not overload 400the successful type with additional meaning of “to be filled” e.g. <code>result<std::optional<T>></code>. 401The decision was therefore taken to disable <em>implicit</em> default construction, but 402still permit <em>explicit</em> default construction by making the programmer spell out their 403intention with extra typing.</p> 404 405<p>To therefore explicitly default construct a <code>result<T></code> or <code>outcome<T></code>, use one 406of these forms as is the most appropriate for the use case:</p> 407 408<ol> 409<li>Construct with just <code>in_place_type<T></code> e.g. <code>result<T>(in_place_type<T>)</code>.</li> 410<li>Construct via <code>success()</code> e.g. <code>outcome<T>(success())</code>.</li> 411<li>Construct from a <code>void</code> form e.g. <code>result<T>(result<void>(in_place_type<void>))</code>.</li> 412</ol> 413 414<h2 id="how-far-away-from-the-proposed-std-expected-t-e-is-outcome-s-checked-t-e">How far away from the proposed <code>std::expected<T, E></code> is Outcome’s <code>checked<T, E></code>?</h2> 415 416<p>Not far, in fact after the first Boost.Outcome peer review in May 2017, Expected moved 417much closer to Outcome, and Outcome deliberately provides <a href="./reference/aliases/checked.html" class="api-reference"><code>checked<T, E = varies></code></a> 418 419as a semantic equivalent.</p> 420 421<p>Here are the remaining differences which represent the 422divergence of consensus opinion between the Boost peer review and WG21 on the proper 423design for this object:</p> 424 425<ol> 426<li><code>checked<T, E></code> has no default constructor. Expected has a default constructor if 427<code>T</code> has a default constructor.</li> 428<li><code>checked<T, E></code> uses the same constructor design as <code>std::variant<...></code>. Expected 429uses the constructor design of <code>std::optional<T></code>.</li> 430<li><code>checked<T, E></code> cannot be modified after construction except by assignment. 431Expected provides an <code>.emplace()</code> modifier.</li> 432<li><code>checked<T, E></code> permits implicit construction from both <code>T</code> and <code>E</code> when 433unambiguous. Expected permits implicit construction from <code>T</code> alone.</li> 434<li><code>checked<T, E></code> does not permit <code>T</code> and <code>E</code> to be the same, and becomes annoying 435to use if they are constructible into one another (implicit construction self-disables). 436Expected permits <code>T</code> and <code>E</code> to be the same.</li> 437<li><code>checked<T, E></code> throws <code>bad_result_access_with<E></code> instead of Expected’s 438<code>bad_expected_access<E></code>.</li> 439<li><code>checked<T, E></code> models <code>std::variant<...></code>. Expected models <code>std::optional<T></code>. Thus: 440 441<ul> 442<li><code>checked<T, E></code> does not provide <code>operator*()</code> nor <code>operator-></code></li> 443<li><code>checked<T, E></code> <code>.error()</code> is wide (i.e. throws on no-value) like <code>.value()</code>. 444Expected’s <code>.error()</code> is narrow (UB on no-error). [<code>checked<T, E></code> provides 445<code>.assume_value()</code> and <code>.assume_error()</code> for narrow (UB causing) observers].</li> 446</ul></li> 447<li><code>checked<T, E></code> uses <code>success<T></code> and <code>failure<E></code> type sugars for disambiguation. 448Expected uses <code>unexpected<E></code> only.</li> 449<li><code>checked<T, E></code> requires <code>E</code> to be default constructible.</li> 450<li><code>checked<T, E></code> defaults <code>E</code> to <code>std::error_code</code> or <code>boost::system::error_code</code>. 451Expected does not default <code>E</code>.</li> 452</ol> 453 454<p>In fact, the two are sufficiently close in design that a highly conforming <code>expected<T, E></code> 455can be implemented by wrapping up <code>checked<T, E></code> with the differing functionality:</p> 456 457<div class="code-snippet"><div class="highlight"><pre class="chroma"><code class="language-c++" data-lang="c++"><span class="cm">/* Here is a fairly conforming implementation of P0323R3 `expected<T, E>` using `checked<T, E>`. 458</span><span class="cm">It passes the reference test suite for P0323R3 at 459</span><span class="cm">https://github.com/viboes/std-make/blob/master/test/expected/expected_pass.cpp with modifications 460</span><span class="cm">only to move the test much closer to the P0323R3 Expected, as the reference test suite is for a 461</span><span class="cm">much older proposed Expected. 462</span><span class="cm"> 463</span><span class="cm">Known differences from P0323R3 in this implementation: 464</span><span class="cm">- `T` and `E` cannot be the same type. 465</span><span class="cm">- `E` must be default constructible. 466</span><span class="cm">- No variant storage is implemented (note the Expected proposal does not actually require this). 467</span><span class="cm">*/</span> 468 469<span class="k">namespace</span> <span class="n">detail</span> 470<span class="p">{</span> 471 <span class="k">template</span> <span class="o"><</span><span class="k">class</span><span class="err"> </span><span class="nc">T</span><span class="p">,</span> <span class="k">class</span><span class="err"> </span><span class="nc">E</span><span class="o">></span> <span class="k">using</span> <span class="n">expected_result</span> <span class="o">=</span> <span class="n">BOOST_OUTCOME_V2_NAMESPACE</span><span class="o">::</span><span class="n">checked</span><span class="o"><</span><span class="n">T</span><span class="p">,</span> <span class="n">E</span><span class="o">></span><span class="p">;</span> 472 <span class="k">template</span> <span class="o"><</span><span class="k">class</span><span class="err"> </span><span class="nc">T</span><span class="p">,</span> <span class="k">class</span><span class="err"> </span><span class="nc">E</span><span class="o">></span> <span class="k">struct</span> <span class="nl">enable_default_constructor</span> <span class="p">:</span> <span class="k">public</span> <span class="n">expected_result</span><span class="o"><</span><span class="n">T</span><span class="p">,</span> <span class="n">E</span><span class="o">></span> 473 <span class="p">{</span> 474 <span class="k">using</span> <span class="n">base</span> <span class="o">=</span> <span class="n">expected_result</span><span class="o"><</span><span class="n">T</span><span class="p">,</span> <span class="n">E</span><span class="o">></span><span class="p">;</span> 475 <span class="k">using</span> <span class="n">base</span><span class="o">::</span><span class="n">base</span><span class="p">;</span> 476 <span class="k">constexpr</span> <span class="nf">enable_default_constructor</span><span class="p">()</span> 477 <span class="o">:</span> <span class="n">base</span><span class="p">{</span><span class="n">BOOST_OUTCOME_V2_NAMESPACE</span><span class="o">::</span><span class="n">in_place_type</span><span class="o"><</span><span class="n">T</span><span class="o">></span><span class="p">}</span> 478 <span class="p">{</span> 479 <span class="p">}</span> 480 <span class="p">};</span> 481 <span class="k">template</span> <span class="o"><</span><span class="k">class</span><span class="err"> </span><span class="nc">T</span><span class="p">,</span> <span class="k">class</span><span class="err"> </span><span class="nc">E</span><span class="o">></span> <span class="k">using</span> <span class="n">select_expected_base</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">conditional_t</span><span class="o"><</span><span class="n">std</span><span class="o">::</span><span class="n">is_default_constructible</span><span class="o"><</span><span class="n">T</span><span class="o">>::</span><span class="n">value</span><span class="p">,</span> <span class="n">enable_default_constructor</span><span class="o"><</span><span class="n">T</span><span class="p">,</span> <span class="n">E</span><span class="o">></span><span class="p">,</span> <span class="n">expected_result</span><span class="o"><</span><span class="n">T</span><span class="p">,</span> <span class="n">E</span><span class="o">>></span><span class="p">;</span> 482<span class="p">}</span> 483<span class="k">template</span> <span class="o"><</span><span class="k">class</span><span class="err"> </span><span class="nc">T</span><span class="p">,</span> <span class="k">class</span><span class="err"> </span><span class="nc">E</span><span class="o">></span> <span class="k">class</span><span class="err"> </span><span class="nc">expected</span> <span class="o">:</span> <span class="k">public</span> <span class="n">detail</span><span class="o">::</span><span class="n">select_expected_base</span><span class="o"><</span><span class="n">T</span><span class="p">,</span> <span class="n">E</span><span class="o">></span> 484<span class="p">{</span> 485 <span class="k">static_assert</span><span class="p">(</span><span class="o">!</span><span class="n">std</span><span class="o">::</span><span class="n">is_same</span><span class="o"><</span><span class="n">T</span><span class="p">,</span> <span class="n">E</span><span class="o">>::</span><span class="n">value</span><span class="p">,</span> <span class="s">"T and E cannot be the same in this expected implementation"</span><span class="p">);</span> 486 <span class="k">using</span> <span class="n">base</span> <span class="o">=</span> <span class="n">detail</span><span class="o">::</span><span class="n">select_expected_base</span><span class="o"><</span><span class="n">T</span><span class="p">,</span> <span class="n">E</span><span class="o">></span><span class="p">;</span> 487 488<span class="k">public</span><span class="o">:</span> 489 <span class="c1">// Inherit base's constructors 490</span><span class="c1"></span> <span class="k">using</span> <span class="n">base</span><span class="o">::</span><span class="n">base</span><span class="p">;</span> 491 <span class="n">expected</span><span class="p">()</span> <span class="o">=</span> <span class="k">default</span><span class="p">;</span> 492 493 <span class="c1">// Expected takes in_place not in_place_type 494</span><span class="c1"></span> <span class="k">template</span> <span class="o"><</span><span class="k">class</span><span class="err">... </span><span class="nc">Args</span><span class="o">></span> 495 <span class="k">constexpr</span> <span class="k">explicit</span> <span class="n">expected</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">in_place_t</span> <span class="cm">/*unused*/</span><span class="p">,</span> <span class="n">Args</span> <span class="o">&&</span><span class="p">...</span> <span class="n">args</span><span class="p">)</span> 496 <span class="o">:</span> <span class="n">base</span><span class="p">{</span><span class="n">BOOST_OUTCOME_V2_NAMESPACE</span><span class="o">::</span><span class="n">in_place_type</span><span class="o"><</span><span class="n">T</span><span class="o">></span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">forward</span><span class="o"><</span><span class="n">Args</span><span class="o">></span><span class="p">(</span><span class="n">args</span><span class="p">)...}</span> 497 <span class="p">{</span> 498 <span class="p">}</span> 499 500 <span class="c1">// Expected always accepts a T even if ambiguous 501</span><span class="c1"></span> <span class="n">BOOST_OUTCOME_TEMPLATE</span><span class="p">(</span><span class="k">class</span><span class="err"> </span><span class="nc">U</span><span class="p">)</span> 502 <span class="n">BOOST_OUTCOME_TREQUIRES</span><span class="p">(</span><span class="n">BOOST_OUTCOME_TPRED</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">is_constructible</span><span class="o"><</span><span class="n">T</span><span class="p">,</span> <span class="n">U</span><span class="o">>::</span><span class="n">value</span><span class="p">))</span> 503 <span class="k">constexpr</span> <span class="n">expected</span><span class="p">(</span><span class="n">U</span> <span class="o">&&</span><span class="n">v</span><span class="p">)</span> 504 <span class="o">:</span> <span class="n">base</span><span class="p">{</span><span class="n">BOOST_OUTCOME_V2_NAMESPACE</span><span class="o">::</span><span class="n">in_place_type</span><span class="o"><</span><span class="n">T</span><span class="o">></span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">forward</span><span class="o"><</span><span class="n">U</span><span class="o">></span><span class="p">(</span><span class="n">v</span><span class="p">)}</span> 505 <span class="p">{</span> 506 <span class="p">}</span> 507 508 <span class="c1">// Expected has an emplace() modifier 509</span><span class="c1"></span> <span class="k">template</span> <span class="o"><</span><span class="k">class</span><span class="err">... </span><span class="nc">Args</span><span class="o">></span> <span class="kt">void</span> <span class="n">emplace</span><span class="p">(</span><span class="n">Args</span> <span class="o">&&</span><span class="p">...</span> <span class="n">args</span><span class="p">)</span> <span class="p">{</span> <span class="o">*</span><span class="k">static_cast</span><span class="o"><</span><span class="n">base</span> <span class="o">*></span><span class="p">(</span><span class="k">this</span><span class="p">)</span> <span class="o">=</span> <span class="n">base</span><span class="p">{</span><span class="n">BOOST_OUTCOME_V2_NAMESPACE</span><span class="o">::</span><span class="n">in_place_type</span><span class="o"><</span><span class="n">T</span><span class="o">></span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">forward</span><span class="o"><</span><span class="n">Args</span><span class="o">></span><span class="p">(</span><span class="n">args</span><span class="p">)...};</span> <span class="p">}</span> 510 511 <span class="c1">// Expected has a narrow operator* and operator-> 512</span><span class="c1"></span> <span class="k">constexpr</span> <span class="k">const</span> <span class="n">T</span> <span class="o">&</span><span class="k">operator</span><span class="o">*</span><span class="p">()</span> <span class="k">const</span> <span class="o">&</span> <span class="p">{</span> <span class="k">return</span> <span class="n">base</span><span class="o">::</span><span class="n">assume_value</span><span class="p">();</span> <span class="p">}</span> 513 <span class="k">constexpr</span> <span class="n">T</span> <span class="o">&</span><span class="k">operator</span><span class="o">*</span><span class="p">()</span> <span class="o">&</span> <span class="p">{</span> <span class="k">return</span> <span class="n">base</span><span class="o">::</span><span class="n">assume_value</span><span class="p">();</span> <span class="p">}</span> 514 <span class="k">constexpr</span> <span class="k">const</span> <span class="n">T</span> <span class="o">&&</span><span class="k">operator</span><span class="o">*</span><span class="p">()</span> <span class="k">const</span> <span class="o">&&</span> <span class="p">{</span> <span class="k">return</span> <span class="n">base</span><span class="o">::</span><span class="n">assume_value</span><span class="p">();</span> <span class="p">}</span> 515 <span class="k">constexpr</span> <span class="n">T</span> <span class="o">&&</span><span class="k">operator</span><span class="o">*</span><span class="p">()</span> <span class="o">&&</span> <span class="p">{</span> <span class="k">return</span> <span class="n">base</span><span class="o">::</span><span class="n">assume_value</span><span class="p">();</span> <span class="p">}</span> 516 <span class="k">constexpr</span> <span class="k">const</span> <span class="n">T</span> <span class="o">*</span><span class="k">operator</span><span class="o">-></span><span class="p">()</span> <span class="k">const</span> <span class="p">{</span> <span class="k">return</span> <span class="o">&</span><span class="n">base</span><span class="o">::</span><span class="n">assume_value</span><span class="p">();</span> <span class="p">}</span> 517 <span class="k">constexpr</span> <span class="n">T</span> <span class="o">*</span><span class="k">operator</span><span class="o">-></span><span class="p">()</span> <span class="p">{</span> <span class="k">return</span> <span class="o">&</span><span class="n">base</span><span class="o">::</span><span class="n">assume_value</span><span class="p">();</span> <span class="p">}</span> 518 519 <span class="c1">// Expected has a narrow error() observer 520</span><span class="c1"></span> <span class="k">constexpr</span> <span class="k">const</span> <span class="n">E</span> <span class="o">&</span><span class="n">error</span><span class="p">()</span> <span class="k">const</span> <span class="o">&</span> <span class="p">{</span> <span class="k">return</span> <span class="n">base</span><span class="o">::</span><span class="n">assume_error</span><span class="p">();</span> <span class="p">}</span> 521 <span class="k">constexpr</span> <span class="n">E</span> <span class="o">&</span><span class="n">error</span><span class="p">()</span> <span class="o">&</span> <span class="p">{</span> <span class="k">return</span> <span class="n">base</span><span class="o">::</span><span class="n">assume_error</span><span class="p">();</span> <span class="p">}</span> 522 <span class="k">constexpr</span> <span class="k">const</span> <span class="n">E</span> <span class="o">&&</span><span class="n">error</span><span class="p">()</span> <span class="k">const</span> <span class="o">&&</span> <span class="p">{</span> <span class="k">return</span> <span class="n">base</span><span class="o">::</span><span class="n">assume_error</span><span class="p">();</span> <span class="p">}</span> 523 <span class="k">constexpr</span> <span class="n">E</span> <span class="o">&</span><span class="n">error</span><span class="p">()</span> <span class="o">&&</span> <span class="p">{</span> <span class="k">return</span> <span class="n">base</span><span class="o">::</span><span class="n">assume_error</span><span class="p">();</span> <span class="p">}</span> 524<span class="p">};</span> 525<span class="k">template</span> <span class="o"><</span><span class="k">class</span><span class="err"> </span><span class="nc">E</span><span class="o">></span> <span class="k">class</span><span class="err"> </span><span class="nc">expected</span><span class="o"><</span><span class="kt">void</span><span class="p">,</span> <span class="n">E</span><span class="o">></span> <span class="o">:</span> <span class="k">public</span> <span class="n">BOOST_OUTCOME_V2_NAMESPACE</span><span class="o">::</span><span class="n">result</span><span class="o"><</span><span class="kt">void</span><span class="p">,</span> <span class="n">E</span><span class="p">,</span> <span class="n">BOOST_OUTCOME_V2_NAMESPACE</span><span class="o">::</span><span class="n">policy</span><span class="o">::</span><span class="n">throw_bad_result_access</span><span class="o"><</span><span class="n">E</span><span class="p">,</span> <span class="kt">void</span><span class="o">>></span> 526<span class="p">{</span> 527 <span class="k">using</span> <span class="n">base</span> <span class="o">=</span> <span class="n">BOOST_OUTCOME_V2_NAMESPACE</span><span class="o">::</span><span class="n">result</span><span class="o"><</span><span class="kt">void</span><span class="p">,</span> <span class="n">E</span><span class="p">,</span> <span class="n">BOOST_OUTCOME_V2_NAMESPACE</span><span class="o">::</span><span class="n">policy</span><span class="o">::</span><span class="n">throw_bad_result_access</span><span class="o"><</span><span class="n">E</span><span class="p">,</span> <span class="kt">void</span><span class="o">>></span><span class="p">;</span> 528 529<span class="k">public</span><span class="o">:</span> 530 <span class="c1">// Inherit base constructors 531</span><span class="c1"></span> <span class="k">using</span> <span class="n">base</span><span class="o">::</span><span class="n">base</span><span class="p">;</span> 532 533 <span class="c1">// Expected has a narrow operator* and operator-> 534</span><span class="c1"></span> <span class="k">constexpr</span> <span class="kt">void</span> <span class="k">operator</span><span class="o">*</span><span class="p">()</span> <span class="k">const</span> <span class="p">{</span> <span class="n">base</span><span class="o">::</span><span class="n">assume_value</span><span class="p">();</span> <span class="p">}</span> 535 <span class="k">constexpr</span> <span class="kt">void</span> <span class="k">operator</span><span class="o">-></span><span class="p">()</span> <span class="k">const</span> <span class="p">{</span> <span class="n">base</span><span class="o">::</span><span class="n">assume_value</span><span class="p">();</span> <span class="p">}</span> 536<span class="p">};</span> 537<span class="k">template</span> <span class="o"><</span><span class="k">class</span><span class="err"> </span><span class="nc">E</span><span class="o">></span> <span class="k">using</span> <span class="n">unexpected</span> <span class="o">=</span> <span class="n">BOOST_OUTCOME_V2_NAMESPACE</span><span class="o">::</span><span class="n">failure_type</span><span class="o"><</span><span class="n">E</span><span class="o">></span><span class="p">;</span> 538<span class="k">template</span> <span class="o"><</span><span class="k">class</span><span class="err"> </span><span class="nc">E</span><span class="o">></span> <span class="n">unexpected</span><span class="o"><</span><span class="n">E</span><span class="o">></span> <span class="n">make_unexpected</span><span class="p">(</span><span class="n">E</span> <span class="o">&&</span><span class="n">arg</span><span class="p">)</span> 539<span class="p">{</span> 540 <span class="k">return</span> <span class="n">BOOST_OUTCOME_V2_NAMESPACE</span><span class="o">::</span><span class="n">failure</span><span class="o"><</span><span class="n">E</span><span class="o">></span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">forward</span><span class="o"><</span><span class="n">E</span><span class="o">></span><span class="p">(</span><span class="n">arg</span><span class="p">));</span> 541<span class="p">}</span> 542<span class="k">template</span> <span class="o"><</span><span class="k">class</span><span class="err"> </span><span class="nc">E</span><span class="p">,</span> <span class="k">class</span><span class="err">... </span><span class="nc">Args</span><span class="o">></span> <span class="n">unexpected</span><span class="o"><</span><span class="n">E</span><span class="o">></span> <span class="n">make_unexpected</span><span class="p">(</span><span class="n">Args</span> <span class="o">&&</span><span class="p">...</span> <span class="n">args</span><span class="p">)</span> 543<span class="p">{</span> 544 <span class="k">return</span> <span class="n">BOOST_OUTCOME_V2_NAMESPACE</span><span class="o">::</span><span class="n">failure</span><span class="o"><</span><span class="n">E</span><span class="o">></span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">forward</span><span class="o"><</span><span class="n">Args</span><span class="o">></span><span class="p">(</span><span class="n">args</span><span class="p">)...);</span> 545<span class="p">}</span> 546<span class="k">template</span> <span class="o"><</span><span class="k">class</span><span class="err"> </span><span class="nc">E</span><span class="o">></span> <span class="k">using</span> <span class="n">bad_expected_access</span> <span class="o">=</span> <span class="n">BOOST_OUTCOME_V2_NAMESPACE</span><span class="o">::</span><span class="n">bad_result_access_with</span><span class="o"><</span><span class="n">E</span><span class="o">></span><span class="p">;</span> 547</code></pre></div><a href="https://github.com/boostorg/outcome/tree/master/doc/src/snippets/expected_implementation.cpp#L35" class="code-snippet-url" target="_blank">View this code on Github</a></div> 548 549 550<h2 id="why-doesn-t-outcome-duplicate-std-expected-t-e-s-design">Why doesn’t Outcome duplicate <code>std::expected<T, E></code>’s design?</h2> 551 552<p>There are a number of reasons:</p> 553 554<ol> 555<li><p>Outcome is not aimed at the same audience as Expected. We target developers 556and users who would be happy to use Boost. Expected targets the standard library user.</p></li> 557 558<li><p>Outcome believes that the monadic use case isn’t as important as Expected does. 559Specifically, we think that 99% of use of Expected in the real world will be to 560return failure from functions, and not as some sort of enhanced or “rich” Optional. 561Outcome therefore models a subset of Variant, whereas Expected models an extended Optional.</p></li> 562 563<li><p>Outcome believes that if you are thinking about using something like Outcome, 564then for you writing failure code will be in the same proportion as writing success code, 565and thus in Outcome writing for failure is exactly the same as writing for success. 566Expected assumes that success will be more common than failure, and makes you type 567more when writing for failure.</p></li> 568 569<li><p>Outcome goes to considerable effort to help the end user type fewer characters 570during use. This results in tighter, less verbose, more succinct code. The cost of this is a steeper 571learning curve and more complex mental model than when programming with Expected.</p></li> 572 573<li><p>Outcome has facilities to make easier interoperation between multiple third 574party libraries each using incommensurate Outcome (or Expected) configurations. Expected does 575not do any of this, but subsequent WG21 papers do propose various interoperation 576mechanisms, <a href="https://wg21.link/P0786">one of which</a> Outcome implements so code using Expected will seamlessly 577interoperate with code using Outcome.</p></li> 578</ol> 579 580<h2 id="is-outcome-riddled-with-undefined-behaviour-for-const-const-containing-and-reference-containing-types">Is Outcome riddled with undefined behaviour for const, const-containing and reference-containing types?</h2> 581 582<p>The short answer is not any more in C++ 20 and after, thanks to changes made to 583C++ 20 at the Belfast WG21 meeting in November 2019.</p> 584 585<p>The longer answer is that before C++ 20, use of placement 586new on types containing <code>const</code> member types where the resulting pointer was 587thrown away is undefined behaviour. As of the resolution of a national body 588comment, this is no longer the case, and now Outcome is free of this particular 589UB for C++ 20 onwards.</p> 590 591<p>This still affects C++ before 20, though no major compiler is affected. Still, 592if you wish to avoid UB, don’t use <code>const</code> types within Outcome types (or any 593<code>optional<T></code>, or <code>vector<T></code> or any STL container type for that matter).</p> 594 595<h3 id="more-detail">More detail</h3> 596 597<p>Before the C++ 14 standard, placement new into storage which used to contain 598a const type was straight out always undefined behaviour, period. Thus all use of 599placement new within a <code>result<const_containing_type></code>, or indeed an <code>optional<const_containing_type></code>, is always 600undefined behaviour before C++ 14. From <code>[basic.life]</code> for the C++ 11 standard:</p> 601 602<blockquote> 603<p>Creating a new object at the storage location that a const object with static, 604thread, or automatic storage duration occupies or, at the storage location 605that such a const object used to occupy before its lifetime ended results 606in undefined behavior.</p> 607</blockquote> 608 609<p>This being excessively restrictive, from C++ 14 onwards, <code>[basic_life]</code> now states:</p> 610 611<blockquote> 612<p>If, after the lifetime of an object has ended and before the storage which 613the object occupied is reused or released, a new object is created at the 614storage location which the original object occupied, a pointer that 615pointed to the original object, a reference that referred to the original 616object, or the name of the original object will automatically refer to the 617new object and, once the lifetime of the new object has started, can be 618used to manipulate the new object, if:</p> 619 620<p>— the storage for the new object exactly overlays the storage location which 621 the original object occupied, and</p> 622 623<p>— the new object is of the same type as the original object (ignoring the 624 top-level cv-qualifiers), and</p> 625 626<p>— the type of the original object is not const-qualified, and, if a class type, 627 does not contain any non-static data member whose type is const-qualified 628 or a reference type, and</p> 629 630<p>— neither the original object nor the new object is a potentially-overlapping 631 subobject</p> 632</blockquote> 633 634<p>Leaving aside my personal objections to giving placement new of non-const 635non-reference types magical pointer renaming powers, the upshot is that if 636you want defined behaviour for placement new of types containing const types 637or references, you must store the pointer returned by placement new, and use 638that pointer for all further reference to the newly created object. This 639obviously adds eight bytes of storage to a <code>result<const_containing_type></code>, which is highly 640undesirable given all the care and attention paid to keeping it small. The alternative 641is to use 642<a href="https://en.cppreference.com/w/cpp/utility/launder" class="api-reference" target="_blank"><i class="fa fa-book" aria-hidden="true"></i> <code>std::launder</code></a> 643, which was added in C++ 17, to ‘launder’ 644the storage into which we placement new before each and every use of that 645storage. This forces the compiler to reload the object stored by placement 646new on every occasion, and not assume it can be constant propagated, which 647impacts codegen quality.</p> 648 649<p>As mentioned above, this issue (in so far as it applies to types containing 650user supplied <code>T</code> which might be <code>const</code>) has been resolved as of C++ 20 onwards, 651and it is extremely unlikely that any C++ compiler will act on any UB here in 652C++ 17 or 14 given how much of STL containers would break.</p> 653 654 655 656 </div><p><small>Last revised: February 03, 2020 at 11:32:11 UTC</small></p> 657<hr> 658<div class="spirit-nav"> 659<a accesskey="p" href="./reference/functions/try_throw_std_exception_from_error.html"><img src="./images/prev.png" alt="Prev"></a> 660 <a accesskey="u" href="./index.html"><img src="./images/up.png" alt="Up"></a> 661 <a accesskey="h" href="./index.html"><img src="./images/home.png" alt="Home"></a><a accesskey="n" href="./videos.html"><img src="./images/next.png" alt="Next"></a></div></body> 662</html> 663