1. Revision History
1.1. Revision 0 - February 7th, 2020
- 
     The only release, and the only revision. Ever. 
2. Motivation, Goals and History
Originally, 
This problem became apparent with other parameter and return abstractions such as 
Pointedly: there is strong motivation to have references in base vocabulary types used for both parameters and returns (variant, optional, expected, future, etc.). A lot of existing practice to do so both before and after the standard settled on its current semantics for 
Finally, given the field experience and absolutely lack of implementation experience with certain models as well as contradictions with the fundamental elements of C++, this paper makes a recommendation for one of the models in hopes of moving discourse out of its current deadlock and toward a friendly, sustainable solution for the C++ ecosystem.
2.1. Stuck in the Past
A very large hole is left in many codebases that desire to wrap their non-null optional returns from 
This has forced many library authors in need of a vocabulary "optional" to have to add preprocessor-based switches to use different kinds of optional implementations and has drastically increased implementation burden. For a vocabulary type, 
Eliminating the need for this by including common goals -- standard goals -- would greatly benefit both library developers and the ecosystem at large with which they interact. We cannot get to these common, standard goals if the landscape is not deliberately and methodically explored, hence this paper.
2.2. Surveying the Present
It has been nearly a decade since 
In furthering this goal, a survey of developers from all experience levels and professional/hobbyist tracks was taken. While there are public implementations of 
Furthermore, the C++ ecosystem has not had an honest evaluation of the design space since the conception of the original optional papers and Nevin’s incredible work for 
2.3. Impact for the Future
The question of references being used in vocabulary types is not just for 
The recommendation presented further along in this paper should be extended to the other necessary vocabulary types. Not doing so risks the same degree of questionable design choices for these other types and further indecision that leads to a permeation of implementations that try to do the same things but are subtly incompatible or not very well implemented. Already, 
2.4. Fragmentation
As mentioned previously, another key motivation of this paper is the surprising amount of fragmentation that exists in the C++ community regarding the 
3. Design Considerations
This paper reviews implementation experience, models, and theory around what a composite / wrapper types like 
3.1. The Great Big Table of Behaviors
Below is a succinct synopsis of the options presented in this paper and their comparison with known solutions and alternative implementations. It does not include the totality of the optional API surface, but has the most exemplary pieces. A key for the symbols:
✔️ - Succeeds
🚫 - Compile-Time Error
❌ - Runtime Error
❓ - Implementation Inconsistency (between engaged/unengaged states, runtime behaviors, etc.)
| optional behaviors | |||||
|---|---|---|---|---|---|
| Operation | T | std::reference_wrapper<T> | T& conservative | Recommended: T& rebind | T& assign through | 
| exemplary implementation(s) | ✔️ std::optional nonstd::optional llvm::Optional folly::Optional | ✔️ std::optional nonstd::optional llvm::Optional folly::Optional | ✔️ std::experimental::optional sol::optional | ✔️ boost::optional tl::optional ts::optional_ref | 🚫 ...? | 
|  | ✔️ copy constructs (disengaged: nothing) | ✔️ binds reference (disengaged: nothing) | ✔️ binds reference (disengaged: nothing) | ✔️ binds reference (disengaged: nothing) | ✔️ binds reference (disengaged: nothing) | 
|  | ✔️ move constructs (disengaged: nothing) | ✔️ binds reference (disengaged: nothing) | ✔️ binds reference (disengaged: nothing) | ✔️ binds reference (disengaged: nothing) | ✔️ binds reference (disengaged: nothing) | 
|  | ✔️ (copy) constructs  | ✔️ binds reference | ✔️ binds reference | ✔️ binds reference | ✔️ binds reference | 
|  | ✔️ (move) constructs  | 🚫 compile-time error | 🚫 compile-time error | 🚫 compile-time error | 🚫 compile-time error | 
| engaged | ✔️ overwrites  | ✔️ rebinds data | 🚫 compile-time error | ✔️ rebinds data | ✔️ ❓ overwrites data | 
| disengaged | ️✔️ overwrites data | ✔️ rebinds data (overwrites reference wrapper) | 🚫 compile-time error | ✔️ rebinds data | ✔️ ❓ rebinds data | 
| engaged | ✔️ move-assigns  | 🚫 compile-time error | 🚫 compile-time error | 🚫 compile-time error | 🚫 or ✔️ ❓ compile-time error, or overwrite data? | 
| disengaged | ✔️ constructs  | 🚫 compile-time error | 🚫 compile-time error | 🚫 compile-time error | 🚫 or ❌ ❓ compile-time error, or runtime shenanigans? | 
| engaged | ✔️ overwrites  | 🚫 compile-time error | 🚫 compile-time error | ✔️ rebinds pointing reference | ✔️ ❓ overwrites data | 
| disengaged | ️✔️ overwrites data | ✔️ overwrites data | 🚫 compile-time error | ✔️ rebinds pointing reference | ✔️ ❓ rebinds data | 
| engaged; arg engaged | ✔️ move assign  | ✔️ rebind data | ✔️ rebind data | ✔️ rebind data | ✔️ move assign  | 
| propagation on | ✔️ propagates - deep | ✔️ shallow | ✔️ shallow | ✔️ shallow | ✔️ ❓ propagates - deep | 
| engaged; arg disengaged | ✔️ disengage  | ✔️ disengage  | ✔️ disengage  | ✔️ disengage  | ✔️ disengage  | 
| disengaged; arg disengaged | ✔️ nothing | ✔️ nothing | ✔️ nothing | ✔️ nothing | ✔️ nothing | 
| engaged | ✔️ copy assigns  | ✔️ copy assigns  | ✔️ copy assigns  | ✔️ copy assigns  | ✔️ copy assigns  | 
| disengaged | ❌ runtime error | ❌ runtime error | ❌ runtime error | ❌ runtime error | ❌ runtime error | 
| engaged | ✔️ move assigns  | ✔️ move assigns  | ✔️ move assigns  | ✔️ move assigns  | ✔️ move assigns  | 
| disengaged | ❌ runtime error | ❌ runtime error | ❌ runtime error | ❌ runtime error | ❌ runtime error | 
| engaged | ✔️ calls  | 🚫 compile-time error | ✔️ calls  | ✔️ calls  | ✔️ calls  | 
| disengaged | ❌ runtime error | ❌ runtime error | ❌ runtime error | ❌ runtime error | ❌ runtime error | 
3.2. Problems in the Design Space
As hinted at in the the table, there are numerous design tradeoffs and some that just have outright problems with various optional implementations.
3.2.1. Problems: Assignment Semantics
Assignment semantics produces a level of fervor and almost religious backlash to even discussing 
#include <optional>#include <iostream>int main ( int , char * []) { int x = 5 ; int y = 24 ; std :: optional < int &> opt = x ; opt = y ; std :: cout << "x is " << x << std :: endl ; std :: cout << "y is " << y << std :: endl ; return 0 ; } 
The contention is the "two plausible interpretations". Under what is known as "Rebinding", the following results show:
x is 5 y is 24 
for what is known as "Assign-through", the following results show:
x is 24 y is 24 
This has become an explosive topic in the Committee and the C++ Community in general and is a heavy crux of contention that, unlike comparison operator specification, could not be resolved before the final shipping date of the current 
3.2.2. Problem: Status Quo, but Monadic?
The highest feature request for optional from the conducted survey is monadic operations. This proposal does not go into it: it is detailed in Sy Brand’s [p0798]. However, it is critical to note that many of these chained monadic operations cannot be implemented efficiently without some form of reference handling through the optionals. Lots of implicit and uncontrolled copies can result from long chains of 
Quantifiable performance impact is also easy to achieve. While code using small value types like 
The full code is buildable with CMake and runnable with a pretty graph-visualizer. There are a few things tested here. First, the basic case is tested: cheap values that can fit in registers (integers, basically). Then, tests for another common type are done: 
As a slight addendum and a fail-safe to test if the code went wrong, there are also two sections: one is marked _failure, and this just means that the optional was empty so no work should have been done in the monadic functions. It’s essentially a basic litmus test for “is something haywire happening in the benchmarks right now?”. There is also _success, where the optional was filled with a value and everything worked out.
As part of the benchmark, computed values are checked for legitimacy, to make sure the optimizer doesn’t just toss everything out on us. Our transformation is essentially just multiplying every element in the 
As demonstrated can see, even for tiny vectors, mapping once versus mapping three times produces a noticeable performance impact. This means today’s optimizers -- in a case where the compiler can see the entire tiny function ("transparent") or not -- cannot elide the additional moves and copies in a value-based world. This has significant performance implications for applications wishing to take advantage of Sy Brand’s excellent work for monadic operations and throws some potential shade on other monadic-capable types such as 
4. Implementation/Deployment Experience
§ 5.1 Recommendation: Rebinding, shallow const, deep comparison reference types has long-standing design, implementation, and industry experience with several high-quality implementations and a handful of conference speakers’s talks, as well as significant research put into them. § 4.2 Assign-Through Deployment and Experience has not been reported to see much experience (any experience, currently), other than the author’s anecdotal experimentation as a beginning programmer, further experimentation before this proposal, and a single other Hobby Developer’s long-term C++ project.
4.1. Conservative and Rebinding
The "conservative solution" proposed in [p1175r0] is a much more tame version that has seen implementation experience for at least 6 years in akrzemi/optional, and 4 years in sol2 (albeit the implementation for sol2 has been changed to instead mimic the full "complete" version described by § 5.1 Recommendation: Rebinding, shallow const, deep comparison reference types, migrated over with no complaints or problems with users). The simple version has also existed as the advised compiler-safe subset for Boost.Optional for 15 years, maybe more (this was mostly from warning about the inability of pre-C++11 to delete or restrict r-value bindings and how 
The "complete" version has seen implementation experience for even longer for those who used the full functionality of Boost.Optional. It is also present in Sy Brand’s optional, a number of industry optionals, and the author’s independent optional. The boost mailing list thread on this topic indicates much of the same potential confusion around references, but towards the end there was the realization that due to inconsistencies with how assign-through behaves the behavior of assign-through is far from ideal.
Jonathan Müller’s type_safe has it, but under a different name (
The explicit 
4.2. Assign-Through Deployment and Experience
This variation of the recommended design found below is an assign-through 
Assign-through as a design is harmful to programmers due to its change-of-behavior depending purely on a runtime property (engaged vs. unengaged). As shown in § 3.1 The Great Big Table of Behaviors, many of the other options are much more consistent and have much less questions surrounding the totality of its behavior.
Most notably is the lack of decided semantics for what happens in the engaged state versus the unengaged state with assignment. This version of an 
If r-value assignment is not a compiler error, then the unengaged state with r-value assignment is left, of which there is no good answer: do we throw 
Specifically, with respect to its bug-prone nature, it is effortless to write code using an assign-through optional where something that may or may not be a code smell cannot be detected simply by looking at the code. Consider the following snippet, adapted from [foonathan-optional-problems]:
int foo_bad ( int x , optional < int &> maybe_y ) { int value = 40 ; /* lots of code */ maybe_y = value ; /* lots of code */ return 24 ; } 
In an assign-through world, this code is valid because 
Static analysis tools cannot definitively point to this code as bad: the best it can offer is a warning, because there exists runtime states where this is exactly what the programmer intended to do. It also creates insanely hard to track bugs that are only discoverable under valgrind or ASan-like tools. Undefined behavior because a manifestation of a collection of runtime properties rather than a mistake that can be caught immediately by tools or by the eyes of a code reviewer is the front-running poster child for hard to track "Heisenbugs".
Furthermore, things that neither humans nor static analysis can properly diagnose come from code that looks and behaves innocently. Consider a sparse 
std :: optional < int &> cache :: retrieve ( std :: uint64_t key ) { std :: optional < int &> opt = this -> get_default_resource_maybe (); // blah blah work auto found_a_thing = this -> lookup_resource ( key ); if ( found_a_thing ) { int & resource = this -> get_specific_resource ( found_marker ); // do stuff with resource, return opt = resource ; } return opt ; // optional says if we got something! } 
This code works perfectly fine most of the time, except in the case where it actually finds something related to 
These are all extraordinarily dangerous developer traps waiting to happen with assign-through optionals.
While there is some theory crafting around assign-through optionals and variants, it has zero publicly available implementation or deployment experience, nor any private industrial support as far as the survey shows. Only 2 out of 110 survey responses report using an assign-through optional. Neither point to a repository. One is used for projects and hacks, the other is used for a large company project. It is notable that for the individual who reported using an assign-through optional in a company project, there was no firm conviction behind the code: it is "a lazy implementation" that predates boost, and has not actually had a reference put inside of it ever. (In other words, it does not count as implementation experience.) The only other user to have an assign-through optional in their projects put rebinding optionals on their wish list: the only people that appear to want an assign-through optional are developers that never, ever implemented or used one.
Asides from these two survey respondents, many companies, Boost Users, the Twitter-verse, several Discord servers, the CppLang Slack, and many more e-mails to C++ programmers across the globe probed for real significant use of an assign-through optional. Nobody has reported using a non-rebinding or non-conservative optional solution in their code base to date, or if they do they are not aware of it and have not given this information in the last year for some reason.
This leaves a serious question of the validity and usefulness for assign-through. It may be that in publishing Revision 0 of this paper, individuals who the author could not reach directly or by survey will come out to inform the author of a non-rebinding reference optional that has seen experience and use as a Studio, Company, and/or shop across the globe. The author encourages everyone to please submit actual deployment experience.
However, given the above, assign-through appears to be exactly that: a fanciful unicorn that does not exist except for the sole purpose of creating unnecessary and directionless bikeshed. It is a trap that masks itself in the clothes of syntactic similarity with references while having demonstrably harmful properties that cannot stand up to even basic design principles for Modern C++'s generally thoughtful and bug-proofing abstractions. It represents a foolish consistency for consistency’s sake and there should not be a future in which it exists for C++, whether that is C++20 or C++50.
Similarly, a "deep const" model does not fit for the same reasons stated above.
4.3. Rebind, but Pointer Comparisons
Rebinding with the addition that comparison only compares pointers -- ignoring unspecified behavior of pointer comparisons when not using 
The author appreciates this model but notes that this is extremely similar to Java’s model for how types should behave. Pointers are used for comparisons in references always, and to introduce a pointer-oriented comparison ideology here would not only inconsistent but useless to the point of pushing C++ developers to have to define a special 
template < typename T > bool maybe_comp ( optional < T > left , optional < T > right ) { return left == right ; } 
Under the Argot model:
int x = 5 ; int y = 5 ; int & x_ref = x ; int & y_ref = y ; optional < int > maybe_x = x ; optional < int > maybe_y = y ; optional < const int &> maybe_ref_x = x_ref ; optional < const int &> maybe_ref_y = y_ref ; bool r0 = x == y ; bool r1 = maybe_comp ( maybe_x , maybe_y ); assert ( r0 == r1 ); // assertion passes bool ref_r0 = x_ref == y_ref ; bool ref_r1 = maybe_comp ( maybe_ref_x , maybe_ref_y ); assert ( ref_r0 == ref_r1 ); // assertion fails 
This model does not have intuitive value to the developer, nor does it provide a better base from which generic programming with references -- especially as it relates to return types from e.g. 
Furthermore, note that comparison of a pointer is a strict subset of comparison with a value. For 
This is not a sound programming generic programming model and is in stark contrast with the fundamentals of C++.
4.3.1. But if values are the goal, why not assign-through?
The stark problem with this is the bugs in the mental model from having to fully commit to this paradigm in all aspects of the problem space. This paper does not see a good way to have entirely value semantics or entirely pointer-value (reference) semantics for all operations that is consistent and workable and less prone to bugs for the end user. This is perhaps a demonstration of a greater weakness in C++ with respect to references: the ecosystem and library at large do not support them because they are not regular at all. No strong identity for references were given, save for the small allowance that they do not copy their contents when passed into and returned from subroutines (but do so on raw assignment to and from one another).
A deep comparison in conjunction with a shallow const / rebinding operator is strange to the person who reads all of Elements of Programming and defines the salient properties as the ones that defines how its comparison operators work. There is some room to get away with this from 
4.4. The Other Choice
The other choice is, of course, the current status quo: no specialization. Libraries which take this path are llvm::Optional, core::optional, optional lite, absl::optional and the current 
Many of these do so because they claim to implement the C++17 version of 
was an attempt to implement proposals to the letter. Because it [(optional references)] didn’t make it in, core doesn’t do it. — Isabella Muerte of mnmlstc/core, July 4th, 2018core 
This does not explain all optional implementations like this, however. For example, llvm::Optional does not make it a specialization at all, and its implementation predates the version finally ratified in the standard (it was first introduced to LLVM in 2010, but had existed before then as clang::Optional for quite a few more years).
David Blaikie of 
What this ultimately means is that the Standards Body has effectively poisoned the well of discourse and provided a chilling effect on the ecosystem’s exploration of optionals with references. By not performing due-diligence on § 4.2 Assign-Through Deployment and Experience, an unverified, untested and undeployed design went from being a mythical unicorn that only existed in theory space to an actual community problem. This is objectively terrible for the C++ community and hopefully this paper brings to light the consequences and reignites a healthier and more grounded discussion based on deployment and implementation experience.
4.5. Pointers?
Pointers have long been heralded as the proper way to have a rebindable optional reference. It’s compact in size, already has a 
4.5.1. Temporaries?
Pointers also introduce lifetime and scoping questions and issues: names have to be assigned to temporaries that otherwise had perfect lifetimes that fit exactly the duration of the function call expression: 
This is not a concern rooted in purely hypothetical thought: 2 survey respondents, a handful of e-mail respondents and several individuals on the CppLang Slack and Discord reported significant pain switching from optional references to 
Had reference support before upgrading to std::optional, porting those to pointers was quite some work... — Anonymous, July 9th, 2018
There is an observable and significant difference between having to use a pointer and being able to just have the useful lifetime extension rules apply to something that binds to a 
4.5.2. API Clarity
Pointers with unclear semantics introduce severe user understanding glitches and can lead to programmer bugs due to unclear ownership models. Even programmers in modern C++ struggle deeply with ownership and lifetime, in no small part to the prevalence of pointers in APIs. While LLVM and Clang chose to use pointers for "optional rebindable references", that design decision incurred a huge penalty that the LLVM Community has been paying repeatedly. In some sense, literally: there has been an ongoing offering and a general to-do item to clean up pointer usage in LLVM and Clang as recent as 2017 (the request for using better pointers is gone from the documentation now). Now that they have a well-established convention with clear ownership, it has become harder to mess up.
That unfortunately has not transitioned to all communities.
sol2 has to have a very explicit and loud note in its documentation about how raw pointers are non-owning, complete with a devoted "quick-n-dirty" tutorial entry as well. This has not stopped programmers from shoving raw 
4.5.3. Semantic Quality
A lot of code run under the assumption or behave as if the 
| Semantic Mapping | |||
|---|---|---|---|
| Type | T operation | none operation | nil/null operation | 
|  | ✔️ | ❌ | ❌ | 
|  | ✔️ | ❌ | ❌ | 
|  | ✔️ | ❌ | ✔️ | 
|  | ✔️ | ✔️ | ❌ | 
|  | ✔️ | ✔️ | ❌ | 
|  | ✔️ | ✔️ | ✔️ | 
On the tin, this table looks exactly as one would expect. Unfortunately, the reality and the expectation of users is far, far different when it comes to interoperating with the system and how pointers behave. Users -- for APIs like the v8 engine for interfacing with JavaScript, Python, Lua, Squirrel and other foreign system interfaces -- demand that the 
class foreign_interface { template < typename T , typename Key > T retrieve ( Key && key ) const ; }; 
User’s doing 
This was the source of a deep division in the community as well over how 
| Semantic Mapping | |||
|---|---|---|---|
| Type | T operation | none operation | nil/null operation | 
|  | ✔️ | ❌ | ❌ | 
|  | ✔️ | ❌ | ❌ | 
|  | ✔️ | ✔️ | ✔️ | 
|  | ✔️ | ✔️ | ❌ | 
|  | ✔️ | ✔️ | ❌ | 
|  | ✔️ | ✔️ | ✔️ | 
It is unfortunate that 
While the first table in this section makes it clear that an API can -- and, maybe should -- differentiate between 
5. Recommendations
Originally, this proposal laid out several solutions in hopes that the community would test each of their merits, providing a natural evolution towards a correct choice. Unfortunately, failure to gain consensus in the Committee during the San Diego 2018 has ruled out a conservative consensus and continued unfounded and bad faith advocacy without due diligence has made it impossible to maintain a neutral and community-guided discourse. Chief among the concerns was not fully defining all operations and that a type was for a template which originally had stronger semantics was not a good design. Given the discouragement of the conservative solution but the encouragement in continued scholarship and effort in determining the right semantics, the work has been done here to come up with the following recommendation.
5.1. Recommendation: Rebinding, shallow const, deep comparison reference types
This is the "complete" solution that is seen as a step up from the conservative solution. It is the version that has seen adoption in 
Rebind semantics have the benefit of having no surprises in engaged vs. unengaged states, as shown in § 3.1 The Great Big Table of Behaviors.
int foo_bad ( int x , optional < int &> maybe_y ) { int value = 40 ; /* lots of code */ maybe_y = value ; /* ... */ return 24 ; } 
Now, the moment anyone sees 
This paper also recommends that comparisons should be "deep", in that they compare the values and not the identities of the reference. This model has widespread industry usage and practice, and has so far not caused undue problems for the C++ community measurable from any user feedback or in their use of generic algorithms. While individuals fixated with 
On top of this, the goal is to make the 
To illustrate, there is a library called 
6. Acknowledgements
Thank you James Berrow, whose work reminded me that thorough scholarship is always the right and sound choice to make.