diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 057d1f8..a8e6d82 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -18,6 +18,8 @@ repos: hooks: - id: clang-format types_or: [c++, c] + # Hand-aligned illustrative listing rendered verbatim in the paper. + exclude: '^papers/wg21-latex/implementation\.hpp$' # CMake linting and formatting - repo: https://github.com/BlankSpruce/gersemi-pre-commit diff --git a/docs/human-design-review-guide.md b/docs/human-design-review-guide.md index 619d3fa..3abbf6f 100644 --- a/docs/human-design-review-guide.md +++ b/docs/human-design-review-guide.md @@ -4,7 +4,7 @@ ## What This Is -A complete implementation of `std::expected` (C++26) extended with six template specializations that allow `T` and/or `E` to be reference types, proposed for C++29. The reference semantics follow P2988 (`optional`): rebind on assignment, shallow const, dangling prevention via deleted constructors. +A complete implementation of `std::expected` (C++26) extended to allow `T` and/or `E` to be reference types, proposed for C++29. This is a new `expected` specialization for a reference value, a relaxation of the primary and `void` templates to admit a reference error `E`, and a new `unexpected` — three `expected` class templates in all, each accepting an object or reference error. The reference semantics follow P2988 (`optional`): rebind on assignment, shallow const, dangling prevention via deleted constructors. **File count:** 3 headers, ~4400 lines of implementation, 15 positive test files (469 tests), 54 negative compile tests. @@ -12,37 +12,47 @@ A complete implementation of `std::expected` (C++26) extended with six template --- -## Decision 1: Six Separate Specializations, Zero Abstraction +## Decision 1: Three Class Templates, Reference Errors Folded In, Zero Abstraction ### What was done -The implementation provides six complete, independent partial specializations: +The implementation provides three complete, independent `expected` class +templates, plus two `unexpected` templates. Reference *error* types are not +separate specializations: each `expected` template permits `E` to be an object +type *or* an lvalue reference, and stores the error as an `unexpected` — which +is the pointer-holding `unexpected` when `E` is a reference. So a single +`expected` covers both `expected` and `expected`, and the +primary covers `expected` and `expected`. -| Specialization | Lines | Storage | +| Template | Value storage | Error storage | |----------------|-------|---------| -| `expected` | ~1100 | `union { T val_; E unex_; }` + bool | -| `expected` | ~800 | `union { E unex_; }` + bool | -| `expected` | ~700 | `T*` + `union { E unex_; }` + bool | -| `expected` | ~700 | `union { T val_; }` + `E*` + bool | -| `expected` | ~550 | `T*` + `E*` + bool | -| `expected` | ~450 | `E*` + bool | - -Each specialization is a self-contained class with its own constructors, assignment operators, observers, swap, equality, and monadic operations. There is no shared base class, no CRTP, no mixin, no macro that generates them. +| `expected` (primary) | `union { T val_; unexpected unex_; }` + bool | `unexpected` (pointer when `E` is `E&`) | +| `expected` | `union { unexpected unex_; }` + bool | `unexpected` | +| `expected` | `union { T* val_; unexpected unex_; }` + bool | `unexpected` | +| `unexpected` | — | `E` by value | +| `unexpected` | — | `E*` | + +The static assertion that used to read "`E` must not be a reference" now reads +"`E` must not be an *rvalue* reference," which is the whole of what admits +reference errors. Each template is a self-contained class with its own +constructors, assignment operators, observers, swap, equality, and monadic +operations. There is no shared base class, no CRTP, no mixin, no macro that +generates them. ### Why this matters -The total is ~4300 lines of template code in a single header. A factored implementation using a common base or policy template could plausibly cut this by 40-60%. The monadic operations alone account for 4 operations x 4 ref-qualified overloads x 6 specializations = 96 method definitions that share structural similarity. +The total is ~4300 lines of template code in a single header. A factored implementation using a common base or policy template could plausibly cut this by 40-60%. The monadic operations alone account for 4 operations x 4 ref-qualified overloads across the three templates — dozens of method definitions that share structural similarity. ### The argument for the current approach - Standard library implementations (libstdc++, libc++, MSVC STL) use flat specializations for `optional` and `expected`. Inheritance-based factoring produces incomprehensible error messages. - Each specialization can be read and audited independently against whatever future standard wording emerges. -- When constraints differ subtly between specializations (and they do — `T&` doesn't need `is_nothrow_move_constructible` checks, `E&` doesn't support `unexpected` construction), a shared base must either disable features via SFINAE or push complexity into the base, which hides it rather than removing it. +- When constraints differ subtly between templates (and they do — `T&` doesn't need `is_nothrow_move_constructible` checks, and a reference `E` accepts `unexpected` construction only when `G` is itself a reference), a shared base must either disable features via SFINAE or push complexity into the base, which hides it rather than removing it. ### The argument against -- **Maintenance burden.** A bug in `and_then` must be fixed in 6 places. A new monadic operation (hypothetical `or_transform`) must be added 24 times (4 overloads x 6 specializations). -- **Consistency risk.** Are all 6 specializations actually consistent? Small divergences creep in when hand-copying constraint clauses. The only way to verify is exhaustive side-by-side comparison. +- **Maintenance burden.** A bug in `and_then` must be fixed in three templates. A new monadic operation (hypothetical `or_transform`) must be added 12 times (4 overloads x 3 templates). +- **Consistency risk.** Are all three templates actually consistent, across both object and reference `E`? Small divergences creep in when hand-copying constraint clauses. The only way to verify is exhaustive side-by-side comparison. - **Review fatigue.** A reviewer looking at 4300 lines of structurally similar code will inevitably skim. The 95th `requires` clause gets less scrutiny than the 5th. ### What to decide @@ -172,7 +182,7 @@ Every deleted operation carries a C++26 `= delete("message")` string explaining ### What was done -All four monadic operations (`and_then`, `or_else`, `transform`, `transform_error`) are provided for all six specializations, with four ref-qualified overloads each (`&`, `const&`, `&&`, `const&&`). +All four monadic operations (`and_then`, `or_else`, `transform`, `transform_error`) are provided for all three `expected` templates, with four ref-qualified overloads each (`&`, `const&`, `&&`, `const&&`), for both object and reference `E`. For reference specializations, the callable always receives `T&` (dereferenced pointer), regardless of the expected object's own value category. This means: @@ -208,7 +218,7 @@ std::move(e).and_then(f); // f still gets int&, not int&& ### What to decide -Is the test suite sufficient for standardization review? The positive coverage is good — every operation has basic tests. But the systematic constraint/SFINAE testing that exists for the primary template is absent for three of the four reference specializations. This gap should be closed before submitting to WG21. +Is the test suite sufficient for standardization review? The positive coverage is good — every operation has basic tests. But the systematic constraint/SFINAE testing that exists for the primary template and `expected` is absent for the reference-error (`E&`) configurations of the primary and `void` templates. This gap should be closed before submitting to WG21. --- @@ -216,13 +226,13 @@ Is the test suite sufficient for standardization review? The positive coverage i ### What exists -Everything is in `expected.hpp`. The `unexpected.hpp` and `bad_expected_access.hpp` are separate (matching the standard's `[expected.syn]` structure), but all six specializations live in one file. +Everything is in `expected.hpp`. The `unexpected.hpp` and `bad_expected_access.hpp` are separate (matching the standard's `[expected.syn]` structure), but all three `expected` templates live in one file. ### What to discuss - **4400 lines in one file.** This is at the boundary of manageable. A reviewer can `grep` and navigate, but side-by-side comparison of specializations requires tooling. - **The standard doesn't mandate file structure.** But for a reference implementation intended to demonstrate proposed wording, would splitting `expected_ref.hpp` (or similar) improve reviewability? -- **Compile times.** Every translation unit that includes `expected.hpp` parses all six specializations. For users who only need `expected`, this is wasted work. The module build path (`expected.cppm`) mitigates this but is opt-in and not the default. +- **Compile times.** Every translation unit that includes `expected.hpp` parses all three templates. For users who only need `expected`, this is wasted work. The module build path (`expected.cppm`) mitigates this but is opt-in and not the default. --- diff --git a/papers/.clang-format b/papers/.clang-format new file mode 100644 index 0000000..1291b3c --- /dev/null +++ b/papers/.clang-format @@ -0,0 +1,111 @@ +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: true +AlignConsecutiveDeclarations: true +AlignEscapedNewlines: Left +AlignOperands: true +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: Yes +BinPackArguments: false +BinPackParameters: false +BraceWrapping: + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Custom +BreakBeforeInheritanceComma: false +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeColon +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 79 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + - Regex: '^(<|"(gtest|isl|json)/)' + Priority: 3 + - Regex: '.*' + Priority: 1 +IncludeIsMainRegex: '$' +IndentCaseLabels: false +IndentPPDirectives: None +IndentWidth: 4 +IndentWrappedFunctionNames: false +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: true +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 4 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerAlignment: Left +ReflowComments: true +SortIncludes: false +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterTemplateKeyword: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Auto +TabWidth: 8 +UseTab: Never diff --git a/papers/.gitignore b/papers/.gitignore index 817c814..27bc196 100644 --- a/papers/.gitignore +++ b/papers/.gitignore @@ -20,6 +20,9 @@ _minted/ # Downloaded bibliography (too large to track; regenerate with make curl-bib) wg21.bib +# Generated clean wording (regenerate with make strip-wording) +expected-clean.tex + # Emacs *~ \#*\# diff --git a/papers/D4280R0.tex b/papers/D4280R0.tex index 3dffea4..f13517d 100644 --- a/papers/D4280R0.tex +++ b/papers/D4280R0.tex @@ -1,5 +1,6 @@ \documentclass[a4paper,10pt,oneside,openany,final,article]{memoir} \input{common} +\newcommand{\CppXXIX}{\Cpp{} 2029} \settocdepth{chapter} \usepackage{minted} \usepackage{fontspec} @@ -8,7 +9,7 @@ % \setmonofont{Source Code Pro} \begin{document} -\title{std::expected} +\title{Expected Over References} \author{ Steve Downey \small<\href{mailto:sdowney@gmail.com}{sdowney@gmail.com}> \\ } @@ -25,15 +26,23 @@ \end{flushright} \begin{abstract} - We propose to add a partial specialization of \tcode{std::expected} for - lvalue reference types as the value type: \tcode{expected}. - The specialization holds a pointer to \tcode{T} when engaged and stores - \tcode{E} by value when in error. Assignment has rebind semantics --- - assigning to an engaged \tcode{expected} always rebinds the - reference, never assigns through to the referent. Construction from - temporaries that would dangle is deleted. This mirrors the design of - \tcode{optional} accepted in \cite{P2988R12} and completes reference - support across the standard library's principal sum types. + We propose reference support for \tcode{std::expected}: the value type, + the error type, or both may be an lvalue reference. This takes one new + partial specialization, \tcode{expected}, for a reference value; a + relaxation of the primary \tcode{expected} and of + \tcode{expected} to admit an lvalue-reference error type \tcode{E}, + which today they reject; and a new \tcode{unexpected} to carry one. All + four value/error reference combinations fall out; only the value-reference + case needs a specialization of its own. + + The reference support follows \tcode{optional} \cite{P2988R12} + exactly where the shapes coincide. A reference is held as a pointer. + Assignment rebinds; it never assigns through to the referent. \tcode{const} + is shallow --- the binding is fixed, the referent is not. Constructors that + would bind a reference to a temporary are deleted, with a diagnostic. This + completes reference support across the standard library's principal + fallible sum type, and delivers the second of the two follow-ons that + \cite{P2988R12} named. \end{abstract} \tableofcontents* @@ -44,13 +53,15 @@ \chapter*{Changes Since Last Version} \item \textbf{R0} Initial revision. \end{itemize} -\chapter{Comparison table} +\chapter{Before / After} -\section{Using a raw pointer for a fallible lookup} +The value case first. A lookup that returns a reference to an element of a +container, and can fail, has no satisfying spelling today. -The conventional idiom for a function that searches a container and may fail -is to return a raw pointer, or to take an out-parameter. Neither approach -captures the error reason. +\section{A fallible lookup that returns a reference} + +The conventional idiom is a raw pointer, or an out-parameter. Neither carries +the error reason. \begin{tabular}{ lr } \begin{minipage}[t]{0.45\columnwidth} @@ -76,11 +87,11 @@ \section{Using a raw pointer for a fallible lookup} \end{minipage} \end{tabular} -\section{Using \tcode{expected} as a substitute} +\section{\tcode{expected} as a substitute} -Returning \tcode{expected} is a common workaround. It forces an -extra dereference through the pointer on the happy path, and the interface -still permits a null pointer to escape as a ``success''. +Returning \tcode{expected} is the common workaround. It forces an +extra dereference on the happy path, and it still lets a null pointer escape +as a ``success''. \begin{tabular}{ lr } \begin{minipage}[t]{0.45\columnwidth} @@ -106,10 +117,13 @@ \section{Using \tcode{expected} as a substitute} \end{minipage} \end{tabular} -\section{Using \tcode{expected, E>}} +\section{\tcode{expected, E>}} -\tcode{std::reference_wrapper} avoids the null-pointer escape but is verbose -and requires \tcode{.get()} everywhere the reference is used. +\tcode{std::reference_wrapper} closes the null hole but is verbose, wants +\tcode{.get()} everywhere the reference is used, and --- the part that +matters here --- assigns through on assignment, which is the behavior +\cite{P1683R0} found to be a bug source. It reintroduces the problem the +reference specialization is designed to remove. \begin{tabular}{ lr } \begin{minipage}[t]{0.45\columnwidth} @@ -137,11 +151,11 @@ \section{Using \tcode{expected, E>}} \end{minipage} \end{tabular} -\section{Monadic composition} +\section{Monadic composition is uniform} -The monadic API of \tcode{expected} works uniformly whether \tcode{T} is a -value type or a reference type, letting callers chain operations without -breaking out of the expected idiom. +The monadic API works the same whether \tcode{T} is a value or a reference, so +a chain of fallible steps never has to break out of the \tcode{expected} +idiom. \begin{tabular}{ lr } \begin{minipage}[t]{0.45\columnwidth} @@ -163,210 +177,514 @@ \section{Monadic composition} \end{minipage} \end{tabular} -\chapter{Motivation} - -\tcode{std::expected} \cite{P0323R12}, added in \CppXXIII{}, is a sum -type holding either a value of type \tcode{T} or an error of type \tcode{E}. -Like \tcode{std::optional} before it, the primary template explicitly -prohibits reference types for \tcode{T}; the current working draft contains -the static assertion: +\section{An error you do not own} -\begin{minted}[fontsize=\small]{c++} -static_assert(!std::is_reference_v, - "T must not be a reference (use expected specialization)"); -\end{minted} +The error channel has the same problem, and it is not hypothetical. A parser +reports through a long-lived diagnostics engine; a driver reports through an +error registry. The error is an existing object elsewhere, not a fresh value +to copy into the \tcode{expected}. Today the choices are to copy it or to +smuggle a pointer through \tcode{E}. \tcode{expected}, carrying its +error by \tcode{unexpected}, says what is meant. -The comment points toward the natural next step. +\begin{tabular}{ lr } + \begin{minipage}[t]{0.45\columnwidth} + \begin{minted}[fontsize=\small]{c++} +// copies the whole diagnostic, or +// hides a pointer inside the error type +std::expected +parse(Source&, DiagnosticEngine&); + \end{minted} + \end{minipage} + & + \begin{minipage}[t]{0.45\columnwidth} + \begin{minted}[fontsize=\small]{c++} +std::expected +parse(Source& s, DiagnosticEngine& d) { + if (bad(s)) + return std::unexpected(d.report(s)); + return node_for(s); +} + \end{minted} + \end{minipage} +\end{tabular} -\tcode{std::optional} has been accepted as \cite{P2988R12}. Its -motivation section observes: +None of the ``after'' column is new machinery. Each is the existing +\tcode{expected} API with a reference where an object used to be. -\begin{quote} - Thus, we expect future papers to propose \tcode{std::expected} and - \tcode{std::variant} with the ability to hold references. -\end{quote} +\chapter{The proposal} -This paper delivers the first of those two promises. +\tcode{std::expected} \cite{P0323R12}, added in \CppXXIII{}, holds either +a value of type \tcode{T} or an error of type \tcode{E}. Like +\tcode{std::optional} before it, the primary template forbids reference types; +the working draft carries the static assertion -The design rationale for preferring rebind semantics over assign-through -semantics is well established by \cite{P2988R12} and the research surveyed -in \cite{P1683R0}. All library implementations that have attempted -assign-through semantics for optional-like types over references have -eventually abandoned that approach due to the bug-prone behavior it -introduces. The same applies to \tcode{expected}. +\begin{minted}[fontsize=\small]{c++} +static_assert(!std::is_reference_v, + "T must not be a reference (use expected specialization)"); +\end{minted} -Lookup functions that return references to elements of a container are -common in high-performance code precisely because they avoid copying -potentially large objects. When such a lookup can fail, there is no -satisfying workaround available today: +whose comment already points at the answer. Reference values need a +specialization; reference errors do not. In full, the proposal is: \begin{itemize} -\item A raw pointer loses the error value and type safety. -\item \tcode{expected} requires two checks and two dereferences on - the happy path, and does not prevent the caller from returning a null - \tcode{T*} in the success channel. -\item \tcode{expected, E>} works but is verbose; - \tcode{.get()} is required everywhere the value is used, and - \tcode{reference_wrapper} does not participate in the monadic API as - naturally as a reference would. +\item a new partial specialization \tcode{expected} for a reference + value, in which \tcode{E} may be an object type or an lvalue reference; +\item a relaxation of the primary \tcode{expected} and of + \tcode{expected}, whose error type \tcode{E} may now be an lvalue + reference where today it may not; and +\item a new partial specialization \tcode{unexpected}, so that + \tcode{unexpected} can carry a reference error. \end{itemize} -\tcode{expected} fills this gap cleanly. It participates in the -monadic API of \tcode{expected} (\tcode{and_then}, \tcode{transform}, -\tcode{or_else}, \tcode{transform_error}) without any awkward adapters. - -There is a principled reason the standard chose to disallow reference types -for sum types: the semantics of assignment differ subtly between value types -and reference types. For the primary template, \tcode{optional} has pure -value semantics; assignment copies or moves the stored value. For -\tcode{optional}, assignment rebinds the pointer. The observable -behavior is different. However, this difference is not a defect --- it is -the same distinction that exists between \tcode{int} and \tcode{int*}, or -between \tcode{tuple} and \tcode{tuple}. The standard already -accommodates reference types in \tcode{std::tuple}, \tcode{std::pair}, and -\tcode{std::reference_wrapper}. Sum types should be no different. - -The relationship between sum types and product types matters here. -\tcode{optional} is equivalent to \tcode{variant}; -\tcode{expected} is equivalent to \tcode{variant}. Neither -\tcode{variant} nor \tcode{tuple} can hold references today, but that is a -separate defect to address separately. \tcode{expected} can be -defined precisely and usefully now, following the exact same pattern as -\tcode{optional}. - -\chapter{Design} - -The design of \tcode{expected} follows \tcode{optional} -\cite{P2988R12} closely. Where the designs diverge it is because -\tcode{expected} carries an error value rather than simply being absent. - -\section{Storage} - -\tcode{expected} stores a \tcode{T*} pointer when engaged and an -\tcode{E} value in-place when in the error state. The default constructor -is deleted --- an engaged \tcode{expected} must always hold a valid -reference, and there is no default reference. - -\section{Rebind Semantics on Assignment} +That is one new \tcode{expected} specialization, not four: an error you do not +own is stored as the pointer already inside \tcode{unexpected}, so +permitting a reference \tcode{E} costs a constraint, not a class. The primary +\tcode{expected} and \tcode{expected} are otherwise unchanged. +A reference is stored as a pointer --- \tcode{T\&} as a \tcode{T*}, a reference +error as the pointer within \tcode{unexpected} --- and there is no new +state and no new vocabulary; the observers, the modifiers, the monadic +operations, and the comparisons keep their names and their meanings. + +An illustrative synopsis of the one new specialization follows. It is the +primary template with the value slot turned into a pointer and the operations a +reference cannot support deleted, and nothing else. + +\begin{codeblock} +namespace std { +template +class expected { + public: + using value_type = T&; + using error_type = E; + using unexpected_type = unexpected; + template + using rebind = expected; + + // constructors + constexpr expected() = delete; + constexpr expected(const expected&); + constexpr expected(expected&&) noexcept(@\seebelow@); + template + constexpr explicit(@\seebelow@) expected(U&&); + template + constexpr expected(U&&) = delete; + template + constexpr explicit(@\seebelow@) expected(const expected&); + template + constexpr explicit(@\seebelow@) expected(expected&&); + template + constexpr explicit(@\seebelow@) expected(const unexpected&); + template + constexpr explicit(@\seebelow@) expected(unexpected&&); + template + constexpr expected(in_place_t, Args&&...) = delete; + template + constexpr explicit expected(unexpect_t, Args&&...); + template + constexpr explicit expected(unexpect_t, initializer_list, Args&&...); + + constexpr ~expected(); + + // assignment + constexpr expected& operator=(const expected&); + constexpr expected& operator=(expected&&) noexcept(@\seebelow@); + template + constexpr expected& operator=(U&&); + template + constexpr expected& operator=(const unexpected&); + template + constexpr expected& operator=(unexpected&&); + template + constexpr T& emplace(U&&) noexcept(@\seebelow@); + + constexpr void swap(expected&) noexcept(@\seebelow@); + friend constexpr void swap(expected&, expected&) noexcept(@\seebelow@); + + // observers + constexpr T* operator->() const noexcept; + constexpr T& operator*() const noexcept; + constexpr explicit operator bool() const noexcept; + constexpr bool has_value() const noexcept; + constexpr T& value() const&; + constexpr T& value() &&; + constexpr const E& error() const& noexcept; + constexpr E& error() & noexcept; + constexpr const E&& error() const&& noexcept; + constexpr E&& error() && noexcept; + template> + constexpr remove_cv_t value_or(U&&) const; + template + constexpr E error_or(G&&) const&; + + // monadic operations + template + constexpr auto and_then(F&&) &; + template + constexpr auto and_then(F&&) &&; + template + constexpr auto and_then(F&&) const&; + template + constexpr auto and_then(F&&) const&&; + template + constexpr auto or_else(F&&) &; + template + constexpr auto or_else(F&&) &&; + template + constexpr auto or_else(F&&) const&; + template + constexpr auto or_else(F&&) const&&; + template + constexpr auto transform(F&&) &; + template + constexpr auto transform(F&&) &&; + template + constexpr auto transform(F&&) const&; + template + constexpr auto transform(F&&) const&&; + template + constexpr auto transform_error(F&&) &; + template + constexpr auto transform_error(F&&) &&; + template + constexpr auto transform_error(F&&) const&; + template + constexpr auto transform_error(F&&) const&&; + + // equality operators + template + friend constexpr bool operator==(const expected&, const expected&); + template + friend constexpr bool operator==(const expected&, const T2&); + template + friend constexpr bool operator==(const expected&, const unexpected&); + + private: + bool @\exposid{has_val}@; // \expos + union { + T* @\exposid{val}@; // \expos + E @\exposid{unex}@; // \expos + }; +}; +} +\end{codeblock} -Assignment always rebinds the pointer, never assigns through to the referent. -This is the only semantics that is not a source of latent bugs -\cite{P1683R0}. Because an \tcode{expected} in the value state -holds only a pointer, any assignment in the value-to-value case is equivalent -to pointer copy, and the full set of converting assignments can be -synthesized from copy-assignment via implicit construction of a temporary -\tcode{expected} on the right-hand side. +The reference implementation lives in the Beman Project's +\tcode{beman.expected} library \cite{Downey_beman_expected}, and is described +in \nameref{chap:impl}. -\section{Dangling Prevention} +\chapter{Motivation} -Constructors from types \tcode{U} that would bind \tcode{T\&} to a -temporary --- as defined by \tcode{reference_constructs_from_temporary} --- are -explicitly deleted. This mirrors the approach taken for -\tcode{optional} \cite{P2988R12}. +Functions that return a reference to an element of a container are common in +performance-sensitive code, precisely because they do not copy. When such a +function can fail, there is no good return type today: -Deletion of these constructors can cause ambiguity in overload resolution -in the same way it does for \tcode{optional}. The design follows the -precedent set by \tcode{std::tuple} and \tcode{std::pair}: make dangling -calls ill-formed rather than silently eliding candidates. +\begin{itemize} +\item A raw pointer drops the error value and the error type. +\item \tcode{expected} costs two checks and two dereferences on the + happy path, and nothing stops a null \tcode{T*} from being returned as a + success. +\item \tcode{expected, E>} works, but requires + \tcode{.get()} at every use, does not thread through the monadic API as a + reference would, and assigns through on assignment. +\end{itemize} -\section{Value Access} +\tcode{expected} fills the gap. It participates in \tcode{and_then}, +\tcode{transform}, \tcode{or_else}, and \tcode{transform_error} with no +adapter. -\tcode{operator*()} and \tcode{value()} return \tcode{T\&}. Value category -is shallow: the reference stored in the \tcode{expected} is not propagated. -An \tcode{expected\&\&} does not steal from the referent; it copies -the pointer. This matches the behavior of \tcode{optional}. +\tcode{std::optional} was accepted as \cite{P2988R12}. Its motivation +observed: -\section{Error Access} +\begin{quote} + Thus, we expect future papers to propose \tcode{std::expected} and + \tcode{std::variant} with the ability to hold references. +\end{quote} -\tcode{error()} returns a reference to the stored \tcode{E}, with the same -cv-ref qualifications as the primary template. +This is the first of those two. It is also more than the sentence asked for: +\tcode{E} is a template parameter exactly as \tcode{T} is, and an error you do +not own is as ordinary as a value you do not own. Supporting references in the +value position but not the error position would be an arbitrary asymmetry. +So the proposal covers both --- the value position with a specialization, the +error position by letting \tcode{E} be a reference in every one of them. + +There is a principled reason the standard disallowed references in its sum +types: assignment means something different for a reference than for a value. +\tcode{optional} has value semantics and assignment copies; +\tcode{optional} rebinds. The observable behavior differs. However, this +is not a defect --- it is the same difference that already holds between +\tcode{int} and \tcode{int*}, and between \tcode{tuple} and +\tcode{tuple}. The standard already stores references in +\tcode{std::tuple}, \tcode{std::pair}, and \tcode{std::reference_wrapper}. A +fallible sum type is not a special case that has earned an exception. + +\tcode{optional} is \tcode{variant}; \tcode{expected} is +\tcode{variant}. Neither \tcode{variant} nor \tcode{tuple} can hold a +reference today. That is a separate and larger defect, and it should not block +a feature that can be specified precisely now by following \tcode{optional}. + +\chapter{Prior art and precedent} + +The design is not invented here. \tcode{optional} \cite{P2988R12} settled +the hard questions --- rebind versus assign-through, shallow versus deep +\tcode{const}, dangling prevention --- after years of debate, and this paper +copies its answers rather than relitigating them. Where the two designs +diverge, it is only because \tcode{expected} carries an error where +\tcode{optional} carries nothing. + +The choice of rebind semantics rests on JeanHeyd Meneide's survey of +reference-holding optionals \cite{P1683R0, REFBIND}: every production +implementation that shipped assign-through was later withdrawn or rewritten. +The result is not a matter of taste. All other semantics are worse. + +The wider precedent is that the standard already holds references in its +product types. \tcode{tuple}, \tcode{pair}, and +\tcode{reference_wrapper} exist and are used. What is missing is references +in the sum types, and this paper closes half of that gap; \tcode{variant} +is the other half, tracked separately. + +\chapter{Design choices and decisions} + +The implementation was built against a numbered decision log. The decisions +that shape the interface are argued here, so LEWG can poll against the numbers. + +\section{Rebind on assignment, never assign-through (D1)} + +Assignment to an engaged \tcode{expected} rebinds the internal pointer. +It does not assign through to the referent. -\section{value\_or} +\begin{minted}[fontsize=\small]{c++} +int a = 1, b = 2; +std::expected e(a); +e = std::expected(b); // e now refers to b; a is still 1 +\end{minted} -\tcode{value_or} returns \tcode{T} by value, for the same reasons as in -\tcode{optional::value_or} \cite{P2988R12}: returning a reference -would require the alternative to be of type \tcode{T\&} or compatible, which -is overly restrictive. Free-function alternatives such as -\tcode{std::reference_or} \cite{P1255R12} and \tcode{std::reference_or} -\cite{D4270R0} provide reference-returning variants over all nullable types -for callers who need that behavior. +There is a real objection: a programmer arriving from +\tcode{std::reference_wrapper} expects assign-through, because that is exactly +what \tcode{reference_wrapper::operator=} does. Granted. And the objection is +answered by the record: \cite{P2988R12} settled this for \tcode{optional}, +and the survey behind it \cite{P1683R0} is a list of assign-through +implementations that were abandoned. Rebind is the only semantics that is not a +latent bug. \tcode{emplace} rebinds too --- it is the only thing it can do --- +and \tcode{swap} exchanges the two pointers, not the two referents. + +\section{Shallow \tcode{const} (D2)} + +A \tcode{const expected} yields a non-\tcode{const} \tcode{T\&} from +\tcode{operator*}. The binding is \tcode{const}; the referent is not. The model +is \tcode{T* const}, not \tcode{const T*}, and it matches +\tcode{reference_wrapper} and \tcode{optional}. A caller who wants deep +\tcode{const} spells \tcode{expected}. + +The concession here is that a \tcode{const\&} parameter of this type no longer +promises ``I will not touch your data.'' But a reference never made that +promise --- a \tcode{T* const} does not either --- and reading \tcode{const} on +a handle as \tcode{const} on the referent is the mistake, not the design. + +\section{Dangling constructors are deleted, with a diagnostic (D3)} + +Every constructor that would bind \tcode{T\&} (or \tcode{E\&}) to a temporary is +deleted, guarded by \tcode{reference_constructs_from_temporary_v} +\cite{P2255R2}. This follows the \tcode{tuple} and \tcode{pair} precedent: make +the dangling call ill-formed, rather than silently dropping the candidate and +letting a worse overload win. + +One wrinkle is worth stating, because it produces two diagnostics for what +looks like one error. A non-\tcode{const} \tcode{int\&} simply cannot bind a +temporary at all, so \tcode{expected(42)} fails as ``no matching +function.'' A \tcode{const int\&} \emph{can} bind a prvalue, so +\tcode{expected(42)} is where the deleted overload fires and the +message is seen. Both are correct rejections. They do not read identically, and +a reviewer should expect both forms. + +\section{References in the error position, by relaxation not specialization (D4)} + +The error type is a template parameter, and errors are routinely owned +elsewhere --- a diagnostics engine, an error registry, a long-lived context +object. So a reference error must be expressible. It does not, however, need a +class of its own: an \tcode{expected} already stores its error as an +\tcode{unexpected}, and \tcode{unexpected} holds a pointer, so +admitting a reference \tcode{E} is a matter of relaxing a constraint, not +writing a specialization. Every \tcode{expected} template --- the primary, the +\tcode{void} specialization, and the new \tcode{expected} --- changes +its guard from ``\tcode{E} is not a reference'' to ``\tcode{E} is not an +\emph{rvalue} reference,'' and reference errors work throughout. Not being able +to express \tcode{expected} while \tcode{expected} works would +be inconsistent, hostile, and strictly worse than disallowing both. + +\section{\tcode{unexpected}, and the reference-only rule (D5)} + +To construct an \tcode{expected} with a reference error, \tcode{unexpected} has +to be able to hold one, so \tcode{unexpected} is part of the proposal. It +stores a pointer to an external object. + +When the error type is a reference, construction from \tcode{unexpected} is +offered \emph{only when \tcode{G} is itself a reference}, and deleted when +\tcode{G} is a value: -\section{Monadic Operations} +\begin{minted}[fontsize=\small]{c++} +int err = 42; +std::expected a(std::unexpect, err); // OK: binds to err +std::expected b(std::unexpected(err)); // OK: source holds a reference +std::expected c(std::unexpected(42)); // deleted: value would dangle +\end{minted} -\tcode{and_then(f)}: calls \tcode{f} with \tcode{T\&}; returns the result of -\tcode{f}, which must be an \tcode{expected} specialization. +The reason is ownership. An \tcode{unexpected} refers to an object it does +not own, so binding \tcode{E\&} to its \tcode{error()} cannot dangle, even when +the \tcode{unexpected} is a temporary. An \tcode{unexpected} for a value +\tcode{G} owns that \tcode{G}, and binding \tcode{E\&} to it dangles the instant +that --- usually temporary --- \tcode{unexpected} is destroyed. So the safe case +is allowed and the dangling case is deleted. + +This supersedes an earlier, blunter design that deleted \emph{all} +\tcode{unexpected} construction for a reference \tcode{E}, and so threw out +the safe \tcode{unexpected} case with the dangerous one. Rebinding +assignment from \tcode{unexpected} follows the same reference-only rule, +and repoints the stored pointer rather than assigning through to the old +referent. The value-\tcode{G} cliff --- \tcode{unexpected} works, +\tcode{unexpected(42)} does not --- is a likely question, and the deleted +overload's message names it. + +\section{\tcode{value\_or} returns by value; \tcode{error\_or} mirrors it (D6)} + +\tcode{value_or} returns \tcode{T} by value, as +\tcode{optional::value_or} does \cite{P2988R12}: a reference return would +force the alternative to be a \tcode{T\&} or something convertible to one, which +is too restrictive to be useful. \tcode{error_or} returns the error by value on +the same reasoning. Callers who genuinely want a reference-returning fallback +have the free-function forms, \tcode{reference_or} \cite{D4270R0}, which work +over all the nullable types at once. + +For \tcode{expected} there is no value, so \tcode{value_or} is +\tcode{= delete}d with a message rather than merely absent, so the error names +the reason instead of leaving the reader to infer it from ``no member named +\tcode{value_or}.'' + +\section{No default constructor, no in-place value, \tcode{emplace} rebinds (D7)} + +An engaged \tcode{expected} must hold a real reference; there is no null +reference, so there is no default constructor. \tcode{expected(in_place, ...)} +is deleted --- there is nowhere to construct a \tcode{T}. \tcode{emplace} +rebinds the pointer, which is the only thing it can do; nothing is being +``emplaced,'' but the name is kept for parallelism with the primary template. + +\section{Monadic operations are uniform (D8)} + +\tcode{and_then}, \tcode{transform}, \tcode{or_else}, and +\tcode{transform_error} are provided on every specialization. On a reference +specialization the callable is always handed \tcode{T\&} (or the error +\tcode{E\&}), regardless of the value category of the \tcode{expected} it is +called on: the stored thing is a pointer, and an rvalue \tcode{expected} does +not make the borrowed object an rvalue. The four ref-qualified overloads are +kept for parity with the primary template even though, for a reference, they do +the same work. + +\section{Shallow conversions must not steal (D9)} + +Converting a reference-holding \tcode{expected} to a value-holding one copies +the referent; it never moves out of it, even from an rvalue source. +\tcode{std::move} on an \tcode{expected} moves the handle --- a pointer +--- not the object it points at. This is the \tcode{boost::optional} +pitfall, stated as a guarantee callers may rely on rather than a bug they must +avoid: -\tcode{transform(f)}: calls \tcode{f} with \tcode{T\&}; wraps the result in -an \tcode{expected}. +\begin{minted}[fontsize=\small]{c++} +std::string s = "bar"; +std::expected r{s}; +std::expected v{std::move(r)}; // copies; s == "bar" afterwards +\end{minted} -\tcode{or_else(f)}: calls \tcode{f} with the error \tcode{E}; returns the -result of \tcode{f}, which must be an \tcode{expected} specialization. +The internal rule that secures this is move-the-wrapper-then-access --- +\tcode{*std::move(r)}, \tcode{std::move(r).error()} --- never +access-then-move. Deref-then-move, \tcode{std::move(*r)}, would force an rvalue +onto a reference the wrapper does not own, and steal from the caller's object. +Because it is a guarantee and not merely an implementation habit, it belongs in +the design. -\tcode{transform_error(f)}: calls \tcode{f} with the error \tcode{E}; wraps -the new error in an \tcode{expected}. +\section{Deleted functions carry messages (D10)} -All four operations propagate the value or error, whichever is not being -processed, without modification. +Every deleted operation uses \tcode{= delete("...")} \cite{P2573R2} to say what +is wrong and what to do instead --- for instance, ``\tcode{expected: no +default constructor; T\& cannot be null}.'' This raises the language floor for +the reference specializations to \CppXXVI{}; the primary template does not need +it. For a feature whose whole difficulty is explaining why a given +construction is refused, a good diagnostic is worth the floor. -\section{In-Place Construction} +\section{Feature test macro (D11)} -\tcode{expected(std::in_place_t, Args...)} is deleted. There is no place to -construct a \tcode{T} in an \tcode{expected}. In-place error -construction via \tcode{unexpect_t} works as in the primary template, because -the error type \tcode{E} is stored by value. +A feature test macro \tcode{__cpp_lib_expected_ref} is proposed, so code can +detect the reference specializations. -\section{Shallow vs Deep \tcode{const}} +\chapter{Anticipated objections} -\tcode{const expected} permits modification of the referent through -\tcode{operator*()}. Constness is shallow, consistent with the pointer model. -If deep constness is desired, use \tcode{expected}. +\section{``Use \tcode{reference\_wrapper} instead''} -\section{Relationship to \tcode{expected} and \tcode{expected}} +It works, and it is what people write today. But \tcode{.get()} litters every +use, it does not thread through the monadic API as a reference does, and +\tcode{reference_wrapper::operator=} assigns through --- reintroducing exactly +the bug D1 removes. The wrapper is the workaround the reference specialization +retires. -The three specializations --- primary (\tcode{T} object type), void value -(\tcode{expected}), and lvalue reference (\tcode{expected}) ---- share the same error-handling API. Conversion from -\tcode{expected} to \tcode{expected} follows the same -constraints as conversion between \tcode{optional} and -\tcode{optional}. +\section{``References in sum types were banned for a reason''} -\section{Feature Test Macro} +They were, and the reason is real: assignment differs between a value and a +reference. Granted --- and it differs for \tcode{int} versus \tcode{int*}, and +for \tcode{tuple} versus \tcode{tuple}, both of which the standard +ships. The difference is a property of the type the programmer asked for, not a +defect in the container. \tcode{optional} decided this; \tcode{expected} +should decide it the same way. -A feature test macro \tcode{__cpp_lib_expected_ref} is proposed, allowing -code to detect the availability of the reference specializations. +\section{``\tcode{expected} is good enough''} -\chapter{Proposal} +It is two checks and two dereferences on the happy path, and it lets a null +\tcode{T*} escape through the success channel as though it were a value. The +type says less than the code means. -Add a partial specialization \tcode{expected} to the \tcode{} -header, with the design described above. +\section{``Wait for \tcode{variant} and do it all at once''} -The reference implementation is available as part of the Beman Project's -\tcode{beman.expected26} library \cite{Downey_beman_expected}. +\tcode{variant} and \tcode{tuple} cannot hold references today, and fixing that +is a separate, larger job. \tcode{expected} can be specified precisely +right now by following \tcode{optional} line for line. A clean, ready +feature should not wait on an unrelated one. \chapter{Impact on the standard} -A pure library extension. No changes to the language are required. No -existing well-formed code is made ill-formed. - -The change adds a new partial specialization of \tcode{expected}; code that -currently relies on the absence of such a specialization (e.g., by static -assertion) will need to be updated, but such code would necessarily have been -deliberately excluding a feature rather than relying on normal program -behavior. +A pure library extension. No core-language change, and no existing well-formed +program is made ill-formed. The change adds the \tcode{expected} and +\tcode{unexpected} specializations, and widens the error type of the +existing \tcode{expected} templates from an object type to an object type or an +lvalue reference. Widening a constraint only ever admits more programs, so the +only code affected is code that relied on a reference error, or the +\tcode{expected} specialization, being rejected --- a +\tcode{static_assert} against them, say --- which was deliberately excluding the +feature rather than relying on ordinary behavior. \chapter{Wording} -Wording to follow in a subsequent revision. +The proposed wording is a marked edit of the \tcode{[expected]} subclause of the +current working draft, reproduced below: additions are shown \added{like this} +and deletions \removed{like this}. The baseline is checked in verbatim and +edited only in the copy, so the marks are exactly the proposed change and +nothing else. -\chapter{Acknowledgments} +The edits are of two kinds. A new \tcode{[expected.ref]} subclause specifies +\tcode{expected}, following the \tcode{optional} wording of +\cite{P2988R12} --- a synopsis with the value slot a pointer, the deleted +default and in-place constructors, the rebind-semantics assignment, and the +shallow-\tcode{const} observers. Marked edits in place widen the error type of +the primary \tcode{expected} and of \tcode{expected} to admit an +lvalue reference, and add \tcode{unexpected} with the reference-only +construction rule of D5. This revision establishes the baseline; the marked +edits land in the next. + +\include{expected-new} -The design of \tcode{expected} is a direct extension of the work on -\tcode{optional} \cite{P2988R12} by Steve Downey and Peter Sommerlad. -Many thanks to JeanHeyd Meneide for the foundational research in -\cite{P1683R0} and \cite{REFBIND}. +\chapter{Acknowledgments} -Thanks to the contributors to the Beman Project's \tcode{beman.expected26} -reference implementation \cite{Downey_beman_expected}. +\tcode{expected} is a direct extension of the work on +\tcode{optional} \cite{P2988R12} by Steve Downey and Peter Sommerlad. Many +thanks to JeanHeyd Meneide for the foundational reference research +\cite{P1683R0, REFBIND}, and to the contributors to the Beman Project's +\tcode{beman.expected} reference implementation \cite{Downey_beman_expected}. \chapter*{Document history} @@ -379,7 +697,18 @@ \chapter*{Document history} \bibliography{wg21,mybiblio} \backmatter -\chapter*{Implementation} +\chapter{Implementation} +\label{chap:impl} + +The three \tcode{expected} class templates --- the primary, \tcode{expected}, and \tcode{expected} --- and the two \tcode{unexpected} templates +are written as flat, independent definitions: no shared base, no CRTP, no mixin, +matching how libstdc++, libc++, and the MSVC STL write \tcode{optional} and +\tcode{expected}. Each admits a reference error type by permitting \tcode{E} to +be an lvalue reference, stored through \tcode{unexpected}. Inheritance-based +factoring would cut the line count, at the cost of the diagnostics; a reader +auditing proposed wording wants each template legible on its own. The full +source, with its positive and negative-compile test suites, is below. \inputminted{c++}{../include/beman/expected/expected.hpp} diff --git a/papers/Makefile b/papers/Makefile index 2e57783..214c70f 100644 --- a/papers/Makefile +++ b/papers/Makefile @@ -54,11 +54,19 @@ $(DEPS_DIR): .PHONY: papers papers: D4280R0.pdf ## Compile papers -D4280R0.pdf : D4280R0.tex wg21.bib +D4280R0.pdf : D4280R0.tex expected-new.tex wg21.bib %.pdf : %.tex $(DEPS_DIR) | $(VENV) latexmk -f -shell-escape -pdflua -use-make -deps -deps-out=$(DEPS_DIR)/$@.d -MP $< +.PHONY: strip-wording +strip-wording: expected-clean.tex ## Strip change markup to clean [expected] wording + +# expected-clean.tex is the marked-up wording copy with \added/\removed markup +# removed: the text to submit as a pull request against the C++ working draft. +expected-clean.tex: expected-new.tex strip-wording.py + python3 strip-wording.py expected-new.tex -o $@ + define curl_cmd = curl https://wg21.link/index.bib > wg21.bib endef diff --git a/papers/expected-base.tex b/papers/expected-base.tex new file mode 100644 index 0000000..5b48130 --- /dev/null +++ b/papers/expected-base.tex @@ -0,0 +1,2750 @@ +\rSec1[expected]{Expected objects} +\indexlibraryglobal{expected}% + +\rSec2[expected.general]{General} + +\pnum +Subclause \ref{expected} describes the class template \tcode{expected} +that represents expected objects. +An \tcode{expected} object holds +an object of type \tcode{T} or an object of type \tcode{E} and +manages the lifetime of the contained objects. + +\rSec2[expected.syn]{Header \tcode{} synopsis} + +\indexheader{expected}% +\indexlibraryglobal{unexpect_t}% +\indexlibraryglobal{unexpect}% +\begin{codeblock} +// mostly freestanding +namespace std { + // \ref{expected.unexpected}, class template \tcode{unexpected} + template class unexpected; + + // \ref{expected.bad}, class template \tcode{bad_expected_access} + template class bad_expected_access; + + // \ref{expected.bad.void}, specialization for \tcode{void} + template<> class bad_expected_access; + + // in-place construction of unexpected values + struct unexpect_t { + explicit unexpect_t() = default; + }; + inline constexpr unexpect_t unexpect{}; + + // \ref{expected.expected}, class template \tcode{expected} + template class expected; // partially freestanding + + // \ref{expected.void}, partial specialization of \tcode{expected} for \tcode{void} types + template requires is_void_v class expected; // partially freestanding +} +\end{codeblock} + +\rSec2[expected.unexpected]{Class template \tcode{unexpected}} + +\rSec3[expected.un.general]{General} + +\pnum +Subclause \ref{expected.unexpected} describes the class template \tcode{unexpected} +that represents unexpected objects stored in \tcode{expected} objects. + +\indexlibraryglobal{unexpected}% +\begin{codeblock} +namespace std { + template + class unexpected { + public: + // \ref{expected.un.cons}, constructors + constexpr unexpected(const unexpected&) = default; + constexpr unexpected(unexpected&&) = default; + template + constexpr explicit unexpected(Err&&); + template + constexpr explicit unexpected(in_place_t, Args&&...); + template + constexpr explicit unexpected(in_place_t, initializer_list, Args&&...); + + constexpr unexpected& operator=(const unexpected&) = default; + constexpr unexpected& operator=(unexpected&&) = default; + + constexpr const E& error() const & noexcept; + constexpr E& error() & noexcept; + constexpr const E&& error() const && noexcept; + constexpr E&& error() && noexcept; + + constexpr void swap(unexpected& other) noexcept(@\seebelow@); + + template + friend constexpr bool operator==(const unexpected&, const unexpected&); + + friend constexpr void swap(unexpected& x, unexpected& y) noexcept(noexcept(x.swap(y))); + + private: + E @\exposidnc{unex}@; // \expos + }; + + template unexpected(E) -> unexpected; +} +\end{codeblock} + +\pnum +A program that instantiates the definition of \tcode{unexpected} for +a non-object type, +an array type, +a specialization of \tcode{unexpected}, or +a cv-qualified type +is ill-formed. + +\rSec3[expected.un.cons]{Constructors} + +\indexlibraryctor{unexpected}% +\begin{itemdecl} +template + constexpr explicit unexpected(Err&& e); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\begin{itemize} +\item +\tcode{is_same_v, unexpected>} is \tcode{false}; and +\item +\tcode{is_same_v, in_place_t>} is \tcode{false}; and +\item +\tcode{is_constructible_v} is \tcode{true}. +\end{itemize} + +\pnum +\effects +Direct-non-list-initializes \exposid{unex} with \tcode{std::forward(e)}. + +\pnum +\throws +Any exception thrown by the initialization of \exposid{unex}. +\end{itemdescr} + +\indexlibraryctor{unexpected}% +\begin{itemdecl} +template + constexpr explicit unexpected(in_place_t, Args&&... args); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\effects +Direct-non-list-initializes +\exposid{unex} with \tcode{std::forward(args)...}. + +\pnum +\throws +Any exception thrown by the initialization of \exposid{unex}. +\end{itemdescr} + +\indexlibraryctor{unexpected}% +\begin{itemdecl} +template + constexpr explicit unexpected(in_place_t, initializer_list il, Args&&... args); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v\&, Args...>} is \tcode{true}. + +\pnum +\effects +Direct-non-list-initializes +\exposid{unex} with \tcode{il, std::forward(args)...}. + +\pnum +\throws +Any exception thrown by the initialization of \exposid{unex}. +\end{itemdescr} + +\rSec3[expected.un.obs]{Observers} + +\indexlibrarymember{error}{unexpected}% +\begin{itemdecl} +constexpr const E& error() const & noexcept; +constexpr E& error() & noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\exposid{unex}. +\end{itemdescr} + +\indexlibrarymember{error}{unexpected}% +\begin{itemdecl} +constexpr E&& error() && noexcept; +constexpr const E&& error() const && noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\tcode{std::move(\exposid{unex})}. +\end{itemdescr} + +\rSec3[expected.un.swap]{Swap} + +\indexlibrarymember{swap}{unexpected}% +\begin{itemdecl} +constexpr void swap(unexpected& other) noexcept(is_nothrow_swappable_v); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{is_swappable_v} is \tcode{true}. + +\pnum +\effects +Equivalent to: +\tcode{using std::swap; swap(\exposid{unex}, other.\exposid{unex});} +\end{itemdescr} + +\begin{itemdecl} +friend constexpr void swap(unexpected& x, unexpected& y) noexcept(noexcept(x.swap(y))); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_swappable_v} is \tcode{true}. + +\pnum +\effects +Equivalent to \tcode{x.swap(y)}. +\end{itemdescr} + +\rSec3[expected.un.eq]{Equality operator} + +\indexlibrarymember{operator==}{unexpected}% +\begin{itemdecl} +template + friend constexpr bool operator==(const unexpected& x, const unexpected& y); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +The expression \tcode{x.error() == y.error()} is well-formed and +its result is convertible to \tcode{bool}. + +\pnum +\returns +\tcode{x.error() == y.error()}. +\end{itemdescr} + +\rSec2[expected.bad]{Class template \tcode{bad_expected_access}} + +\indexlibraryglobal{bad_expected_access}% +\begin{codeblock} +namespace std { + template + class bad_expected_access : public bad_expected_access { + public: + constexpr explicit bad_expected_access(E); + constexpr const char* what() const noexcept override; + constexpr E& error() & noexcept; + constexpr const E& error() const & noexcept; + constexpr E&& error() && noexcept; + constexpr const E&& error() const && noexcept; + + private: + E @\exposidnc{unex}@; // \expos + }; +} +\end{codeblock} + +\pnum +The class template \tcode{bad_expected_access} +defines the type of objects thrown as exceptions to report the situation +where an attempt is made to access the value of an \tcode{expected} object +for which \tcode{has_value()} is \tcode{false}. + +\indexlibraryctor{bad_expected_access}% +\begin{itemdecl} +constexpr explicit bad_expected_access(E e); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Initializes \exposid{unex} with \tcode{std::move(e)}. +\end{itemdescr} + +\indexlibrarymember{error}{bad_expected_access}% +\begin{itemdecl} +constexpr const E& error() const & noexcept; +constexpr E& error() & noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\exposid{unex}. +\end{itemdescr} + +\indexlibrarymember{error}{bad_expected_access}% +\begin{itemdecl} +constexpr E&& error() && noexcept; +constexpr const E&& error() const && noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\tcode{std::move(\exposid{unex})}. +\end{itemdescr} + +\indexlibrarymember{what}{bad_expected_access}% +\begin{itemdecl} +constexpr const char* what() const noexcept override; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +An \impldef{return value of \tcode{bad_expected_access::what}} \ntbs, +which during constant evaluation is encoded with +the ordinary literal encoding\iref{lex.ccon}. +\end{itemdescr} + +\rSec2[expected.bad.void]{Class template specialization \tcode{bad_expected_access}} + +\begin{codeblock} +namespace std { + template<> + class bad_expected_access : public exception { + protected: + constexpr bad_expected_access() noexcept; + constexpr bad_expected_access(const bad_expected_access&) noexcept; + constexpr bad_expected_access(bad_expected_access&&) noexcept; + constexpr bad_expected_access& operator=(const bad_expected_access&) noexcept; + constexpr bad_expected_access& operator=(bad_expected_access&&) noexcept; + constexpr ~bad_expected_access(); + + public: + constexpr const char* what() const noexcept override; + }; +} +\end{codeblock} + +\indexlibrarymember{what}{bad_expected_access}% +\begin{itemdecl} +constexpr const char* what() const noexcept override; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +An \impldef{return value of \tcode{bad_expected_access::what}} \ntbs, +which during constant evaluation is encoded with +the ordinary literal encoding\iref{lex.ccon}. +\end{itemdescr} + +\rSec2[expected.expected]{Class template \tcode{expected}} + +\rSec3[expected.object.general]{General} + +\begin{codeblock} +namespace std { + template + class expected { + public: + using @\libmember{value_type}{expected}@ = T; + using @\libmember{error_type}{expected}@ = E; + using @\libmember{unexpected_type}{expected}@ = unexpected; + + template + using @\libmember{rebind}{expected}@ = expected; + + // \ref{expected.object.cons}, constructors + constexpr expected(); + constexpr expected(const expected&); + constexpr expected(expected&&) noexcept(@\seebelow@); + template + constexpr explicit(@\seebelow@) expected(const expected&); + template + constexpr explicit(@\seebelow@) expected(expected&&); + + template> + constexpr explicit(@\seebelow@) expected(U&& v); + + template + constexpr explicit(@\seebelow@) expected(const unexpected&); + template + constexpr explicit(@\seebelow@) expected(unexpected&&); + + template + constexpr explicit expected(in_place_t, Args&&...); + template + constexpr explicit expected(in_place_t, initializer_list, Args&&...); + template + constexpr explicit expected(unexpect_t, Args&&...); + template + constexpr explicit expected(unexpect_t, initializer_list, Args&&...); + + // \ref{expected.object.dtor}, destructor + constexpr ~expected(); + + // \ref{expected.object.assign}, assignment + constexpr expected& operator=(const expected&); + constexpr expected& operator=(expected&&) noexcept(@\seebelow@); + template> constexpr expected& operator=(U&&); + template + constexpr expected& operator=(const unexpected&); + template + constexpr expected& operator=(unexpected&&); + + template + constexpr T& emplace(Args&&...) noexcept; + template + constexpr T& emplace(initializer_list, Args&&...) noexcept; + + // \ref{expected.object.swap}, swap + constexpr void swap(expected&) noexcept(@\seebelow@); + friend constexpr void swap(expected& x, expected& y) noexcept(noexcept(x.swap(y))); + + // \ref{expected.object.obs}, observers + constexpr const T* operator->() const noexcept; + constexpr T* operator->() noexcept; + constexpr const T& operator*() const & noexcept; + constexpr T& operator*() & noexcept; + constexpr const T&& operator*() const && noexcept; + constexpr T&& operator*() && noexcept; + constexpr explicit operator bool() const noexcept; + constexpr bool has_value() const noexcept; + constexpr const T& value() const &; // freestanding-deleted + constexpr T& value() &; // freestanding-deleted + constexpr const T&& value() const &&; // freestanding-deleted + constexpr T&& value() &&; // freestanding-deleted + constexpr const E& error() const & noexcept; + constexpr E& error() & noexcept; + constexpr const E&& error() const && noexcept; + constexpr E&& error() && noexcept; + template> constexpr T value_or(U&&) const &; + template> constexpr T value_or(U&&) &&; + template constexpr E error_or(G&&) const &; + template constexpr E error_or(G&&) &&; + + // \ref{expected.object.monadic}, monadic operations + template constexpr auto and_then(F&& f) &; + template constexpr auto and_then(F&& f) &&; + template constexpr auto and_then(F&& f) const &; + template constexpr auto and_then(F&& f) const &&; + template constexpr auto or_else(F&& f) &; + template constexpr auto or_else(F&& f) &&; + template constexpr auto or_else(F&& f) const &; + template constexpr auto or_else(F&& f) const &&; + template constexpr auto transform(F&& f) &; + template constexpr auto transform(F&& f) &&; + template constexpr auto transform(F&& f) const &; + template constexpr auto transform(F&& f) const &&; + template constexpr auto transform_error(F&& f) &; + template constexpr auto transform_error(F&& f) &&; + template constexpr auto transform_error(F&& f) const &; + template constexpr auto transform_error(F&& f) const &&; + + // \ref{expected.object.eq}, equality operators + template requires (!is_void_v) + friend constexpr bool operator==(const expected& x, const expected& y); + template + friend constexpr bool operator==(const expected&, const T2&); + template + friend constexpr bool operator==(const expected&, const unexpected&); + + private: + bool @\exposid{has_val}@; // \expos + union { + remove_cv_t @\exposid{val}@; // \expos + E @\exposid{unex}@; // \expos + }; + }; +} +\end{codeblock} + +\pnum +Any object of type \tcode{expected} either +contains a value of type \tcode{T} or +a value of type \tcode{E} +nested within\iref{intro.object} it. +Member \exposid{has_val} indicates whether the \tcode{expected} object +contains an object of type \tcode{T}. + +\pnum +A type \tcode{T} is a \term{valid value type for \tcode{expected}}, +if \tcode{remove_cv_t} is \tcode{void} +or a complete non-array object type that is not \tcode{in_place_t}, +\tcode{unexpect_t}, +or a specialization of \tcode{unexpected}. +A program which instantiates class template \tcode{expected} +with an argument \tcode{T} that is not a valid value +type for \tcode{expected} is ill-formed. +A program that instantiates +the definition of the template \tcode{expected} +with a type for the \tcode{E} parameter +that is not a valid template argument for \tcode{unexpected} is ill-formed. + +\pnum +When \tcode{T} is not \cv{} \tcode{void}, it shall meet +the \oldconcept{Destructible} requirements (\tref{cpp17.destructible}). +\tcode{E} shall meet +the \oldconcept{Destructible} requirements. + +\rSec3[expected.object.cons]{Constructors} + +\pnum +The exposition-only variable template \exposid{converts-from-any-cvref} +defined in \ref{optional.ctor} +is used by some constructors for \tcode{expected}. + +\indexlibraryctor{expected}% +\begin{itemdecl} +constexpr expected(); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_default_constructible_v} is \tcode{true}. + +\pnum +\effects +Value-initializes \exposid{val}. + +\pnum +\ensures +\tcode{has_value()} is \tcode{true}. + +\pnum +\throws +Any exception thrown by the initialization of \exposid{val}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +constexpr expected(const expected& rhs); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +If \tcode{rhs.has_value()} is \tcode{true}, +direct-non-list-initializes \exposid{val} with \tcode{*rhs}. +Otherwise, direct-non-list-initializes \exposid{unex} with \tcode{rhs.error()}. + +\pnum +\ensures +\tcode{rhs.has_value() == this->has_value()}. + +\pnum +\throws +Any exception thrown by the initialization of \exposid{val} or \exposid{unex}. + +\pnum +\remarks +This constructor is defined as deleted unless +\begin{itemize} +\item +\tcode{is_copy_constructible_v} is \tcode{true} and +\item +\tcode{is_copy_constructible_v} is \tcode{true}. +\end{itemize} + +\pnum +This constructor is trivial if +\begin{itemize} +\item +\tcode{is_trivially_copy_constructible_v} is \tcode{true} and +\item +\tcode{is_trivially_copy_constructible_v} is \tcode{true}. +\end{itemize} +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +constexpr expected(expected&& rhs) noexcept(@\seebelow@); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\begin{itemize} +\item +\tcode{is_move_constructible_v} is \tcode{true} and +\item +\tcode{is_move_constructible_v} is \tcode{true}. +\end{itemize} + +\pnum +\effects +If \tcode{rhs.has_value()} is \tcode{true}, +direct-non-list-initializes \exposid{val} with \tcode{std::move(*rhs)}. +Otherwise, +direct-non-list-initializes \exposid{unex} with \tcode{std::move(rhs.error())}. + +\pnum +\ensures +\tcode{rhs.has_value()} is unchanged; +\tcode{rhs.has_value() == this->has_value()} is \tcode{true}. + +\pnum +\throws +Any exception thrown by the initialization of \exposid{val} or \exposid{unex}. + +\pnum +\remarks +The exception specification is equivalent to +\tcode{is_nothrow_move_constructible_v \&\& +is_nothrow_move_constructible_v}. + +\pnum +This constructor is trivial if +\begin{itemize} +\item +\tcode{is_trivially_move_constructible_v} is \tcode{true} and +\item +\tcode{is_trivially_move_constructible_v} is \tcode{true}. +\end{itemize} +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + constexpr explicit(@\seebelow@) expected(const expected& rhs); +template + constexpr explicit(@\seebelow@) expected(expected&& rhs); +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let: +\begin{itemize} +\item +\tcode{UF} be \tcode{const U\&} for the first overload and +\tcode{U} for the second overload. +\item +\tcode{GF} be \tcode{const G\&} for the first overload and +\tcode{G} for the second overload. +\end{itemize} + +\pnum +\constraints +\begin{itemize} +\item +\tcode{is_constructible_v} is \tcode{true}; and +\item +\tcode{is_constructible_v} is \tcode{true}; and +\item +if \tcode{T} is not \cv{} \tcode{bool}, +\tcode{\exposid{converts-from-any-cvref}>} is \tcode{false}; and +\item +\tcode{is_constructible_v, expected\&>} is \tcode{false}; and +\item +\tcode{is_constructible_v, expected>} is \tcode{false}; and +\item +\tcode{is_constructible_v, const expected\&>} is \tcode{false}; and +\item +\tcode{is_constructible_v, const expected>} is \tcode{false}. +\end{itemize} + +\pnum +\effects +If \tcode{rhs.has_value()}, +direct-non-list-initializes \exposid{val} with \tcode{std::forward(*rhs)}. +Otherwise, +direct-non-list-initializes \exposid{unex} with \tcode{std::forward(rhs.error())}. + +\pnum +\ensures +\tcode{rhs.has_value()} is unchanged; +\tcode{rhs.has_value() == this->has_value()} is \tcode{true}. + +\pnum +\throws +Any exception thrown by the initialization of \exposid{val} or \exposid{unex}. + +\pnum +\remarks +The expression inside \tcode{explicit} is equivalent to +\tcode{!is_convertible_v || !is_convertible_v}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template> + constexpr explicit(!is_convertible_v) expected(U&& v); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\begin{itemize} +\item +\tcode{is_same_v, in_place_t>} is \tcode{false}; and +\item +\tcode{is_same_v, expected>} is \tcode{false}; and +\item +\tcode{is_same_v, unexpect_t>} is \tcode{false}; and +\item +\tcode{remove_cvref_t} is not a specialization of \tcode{unexpected}; and +\item +\tcode{is_constructible_v} is \tcode{true}; and +\item +if \tcode{T} is \cv{} \tcode{bool}, +\tcode{remove_cvref_t} is not a specialization of \tcode{expected}. +\end{itemize} + +\pnum +\effects +Direct-non-list-initializes \exposid{val} with \tcode{std::forward(v)}. + +\pnum +\ensures +\tcode{has_value()} is \tcode{true}. + +\pnum +\throws +Any exception thrown by the initialization of \exposid{val}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + constexpr explicit(!is_convertible_v) expected(const unexpected& e); +template + constexpr explicit(!is_convertible_v) expected(unexpected&& e); +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{GF} be \tcode{const G\&} for the first overload and +\tcode{G} for the second overload. + +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\effects +Direct-non-list-initializes \exposid{unex} with \tcode{std::forward(e.error())}. + +\pnum +\ensures +\tcode{has_value()} is \tcode{false}. + +\pnum +\throws +Any exception thrown by the initialization of \exposid{unex}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + constexpr explicit expected(in_place_t, Args&&... args); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\effects +Direct-non-list-initializes \exposid{val} with \tcode{std::forward(args)...}. + +\pnum +\ensures +\tcode{has_value()} is \tcode{true}. + +\pnum +\throws +Any exception thrown by the initialization of \exposid{val}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + constexpr explicit expected(in_place_t, initializer_list il, Args&&... args); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v\&, Args...>} is \tcode{true}. + +\pnum +\effects +Direct-non-list-initializes \exposid{val} with +\tcode{il, std::forward(args)...}. + +\pnum +\ensures +\tcode{has_value()} is \tcode{true}. + +\pnum +\throws +Any exception thrown by the initialization of \exposid{val}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + constexpr explicit expected(unexpect_t, Args&&... args); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\effects +Direct-non-list-initializes \exposid{unex} with +\tcode{std::forward(args)...}. + +\pnum +\ensures +\tcode{has_value()} is \tcode{false}. + +\pnum +\throws +Any exception thrown by the initialization of \exposid{unex}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + constexpr explicit expected(unexpect_t, initializer_list il, Args&&... args); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v\&, Args...>} is \tcode{true}. + +\pnum +\effects +Direct-non-list-initializes \exposid{unex} with +\tcode{il, std::forward(args)...}. + +\pnum +\ensures +\tcode{has_value()} is \tcode{false}. + +\pnum +\throws +Any exception thrown by the initialization of \exposid{unex}. +\end{itemdescr} + +\rSec3[expected.object.dtor]{Destructor} + +\indexlibrarydtor{expected}% +\begin{itemdecl} +constexpr ~expected(); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +If \tcode{has_value()} is \tcode{true}, destroys \exposid{val}, +otherwise destroys \exposid{unex}. + +\pnum +\remarks +If \tcode{is_trivially_destructible_v} is \tcode{true}, and +\tcode{is_trivially_destructible_v} is \tcode{true}, +then this destructor is a trivial destructor. +\end{itemdescr} + +\rSec3[expected.object.assign]{Assignment} + +\pnum +This subclause makes use of the following exposition-only function template: +\begin{codeblock} +template +constexpr void @\exposid{reinit-expected}@(T& newval, U& oldval, Args&&... args) { // \expos + if constexpr (is_nothrow_constructible_v) { + destroy_at(addressof(oldval)); + construct_at(addressof(newval), std::forward(args)...); + } else if constexpr (is_nothrow_move_constructible_v) { + T tmp(std::forward(args)...); + destroy_at(addressof(oldval)); + construct_at(addressof(newval), std::move(tmp)); + } else { + U tmp(std::move(oldval)); + destroy_at(addressof(oldval)); + try { + construct_at(addressof(newval), std::forward(args)...); + } catch (...) { + construct_at(addressof(oldval), std::move(tmp)); + throw; + } + } +} +\end{codeblock} + +\indexlibrarymember{operator=}{expected}% +\begin{itemdecl} +constexpr expected& operator=(const expected& rhs); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +\begin{itemize} +\item +If \tcode{this->has_value() \&\& rhs.has_value()} is \tcode{true}, +equivalent to \tcode{\exposid{val} = *rhs}. +\item +Otherwise, if \tcode{this->has_value()} is \tcode{true}, equivalent to: +\begin{codeblock} +@\exposid{reinit-expected}@(@\exposid{unex}@, @\exposid{val}@, rhs.error()) +\end{codeblock} +\item +Otherwise, if \tcode{rhs.has_value()} is \tcode{true}, equivalent to: +\begin{codeblock} +@\exposid{reinit-expected}@(@\exposid{val}@, @\exposid{unex}@, *rhs) +\end{codeblock} +\item +Otherwise, equivalent to \tcode{\exposid{unex} = rhs.error()}. +\end{itemize} +Then, if no exception was thrown, +equivalent to: \tcode{\exposid{has_val} = rhs.has_value(); return *this;} + +\pnum +\returns +\tcode{*this}. + +\pnum +\remarks +This operator is defined as deleted unless: +\begin{itemize} +\item +\tcode{is_copy_assignable_v} is \tcode{true} and +\item +\tcode{is_copy_constructible_v} is \tcode{true} and +\item +\tcode{is_copy_assignable_v} is \tcode{true} and +\item +\tcode{is_copy_constructible_v} is \tcode{true} and +\item +\tcode{is_nothrow_move_constructible_v || is_nothrow_move_constructible_v} +is \tcode{true}. +\end{itemize} + +\pnum +This operator is trivial if: +\begin{itemize} +\item \tcode{is_trivially_copy_constructible_v} is \tcode{true}, and +\item \tcode{is_trivially_copy_assignable_v} is \tcode{true}, and +\item \tcode{is_trivially_destructible_v} is \tcode{true}, and +\item \tcode{is_trivially_copy_constructible_v} is \tcode{true}, and +\item \tcode{is_trivially_copy_assignable_v} is \tcode{true}, and +\item \tcode{is_trivially_destructible_v} is \tcode{true}. +\end{itemize} +\end{itemdescr} + +\indexlibrarymember{operator=}{expected}% +\begin{itemdecl} +constexpr expected& operator=(expected&& rhs) noexcept(@\seebelow@); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\begin{itemize} +\item +\tcode{is_move_constructible_v} is \tcode{true} and +\item +\tcode{is_move_assignable_v} is \tcode{true} and +\item +\tcode{is_move_constructible_v} is \tcode{true} and +\item +\tcode{is_move_assignable_v} is \tcode{true} and +\item +\tcode{is_nothrow_move_constructible_v || is_nothrow_move_constructible_v} +is \tcode{true}. +\end{itemize} + +\pnum +\effects +\begin{itemize} +\item +If \tcode{this->has_value() \&\& rhs.has_value()} is \tcode{true}, +equivalent to \tcode{\exposid{val} = std::move(*rhs)}. +\item +Otherwise, if \tcode{this->has_value()} is \tcode{true}, equivalent to: +\begin{codeblock} +@\exposid{reinit-expected}@(@\exposid{unex}@, @\exposid{val}@, std::move(rhs.error())) +\end{codeblock} +\item +Otherwise, if \tcode{rhs.has_value()} is \tcode{true}, equivalent to: +\begin{codeblock} +@\exposid{reinit-expected}@(@\exposid{val}@, @\exposid{unex}@, std::move(*rhs)) +\end{codeblock} +\item +Otherwise, equivalent to \tcode{\exposid{unex} = std::move(rhs.error())}. +\end{itemize} +Then, if no exception was thrown, +equivalent to: \tcode{has_val = rhs.has_value(); return *this;} + +\pnum +\returns +\tcode{*this}. + +\pnum +\remarks +The exception specification is equivalent to: +\begin{codeblock} +is_nothrow_move_assignable_v && is_nothrow_move_constructible_v && +is_nothrow_move_assignable_v && is_nothrow_move_constructible_v +\end{codeblock} + +\pnum +This operator is trivial if: +\begin{itemize} +\item \tcode{is_trivially_move_constructible_v} is \tcode{true}, and +\item \tcode{is_trivially_move_assignable_v} is \tcode{true}, and +\item \tcode{is_trivially_destructible_v} is \tcode{true}, and +\item \tcode{is_trivially_move_constructible_v} is \tcode{true}, and +\item \tcode{is_trivially_move_assignable_v} is \tcode{true}, and +\item \tcode{is_trivially_destructible_v} is \tcode{true}. +\end{itemize} +\end{itemdescr} + +\indexlibrarymember{operator=}{expected}% +\begin{itemdecl} +template> + constexpr expected& operator=(U&& v); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\begin{itemize} +\item +\tcode{is_same_v>} is \tcode{false}; and +\item +\tcode{remove_cvref_t} is not a specialization of \tcode{unexpected}; and +\item +\tcode{is_constructible_v} is \tcode{true}; and +\item +\tcode{is_assignable_v} is \tcode{true}; and +\item +\tcode{is_nothrow_constructible_v || is_nothrow_move_constructible_v ||\newline +is_nothrow_move_constructible_v} +is \tcode{true}. +\end{itemize} + +\pnum +\effects +\begin{itemize} +\item +If \tcode{has_value()} is \tcode{true}, +equivalent to: \tcode{\exposid{val} = std::forward(v);} +\item +Otherwise, equivalent to: +\begin{codeblock} +@\exposid{reinit-expected}@(@\exposid{val}@, @\exposid{unex}@, std::forward(v)); +@\exposid{has_val}@ = true; +\end{codeblock} +\end{itemize} + +\pnum +\returns +\tcode{*this}. +\end{itemdescr} + +\indexlibrarymember{operator=}{expected}% +\begin{itemdecl} +template + constexpr expected& operator=(const unexpected& e); +template + constexpr expected& operator=(unexpected&& e); +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{GF} be \tcode{const G\&} for the first overload and +\tcode{G} for the second overload. + +\pnum +\constraints +\begin{itemize} +\item +\tcode{is_constructible_v} is \tcode{true}; and +\item +\tcode{is_assignable_v} is \tcode{true}; and +\item +\tcode{is_nothrow_constructible_v || is_nothrow_move_constructible_v ||\newline +is_nothrow_move_constructible_v} is \tcode{true}. +\end{itemize} + +\pnum +\effects +\begin{itemize} +\item +If \tcode{has_value()} is \tcode{true}, equivalent to: +\begin{codeblock} +@\exposid{reinit-expected}@(@\exposid{unex}@, @\exposid{val}@, std::forward(e.error())); +@\exposid{has_val}@ = false; +\end{codeblock} +\item +Otherwise, equivalent to: +\tcode{\exposid{unex} = std::forward(e.error());} +\end{itemize} + +\pnum +\returns +\tcode{*this}. +\end{itemdescr} + +\indexlibrarymember{emplace}{expected}% +\begin{itemdecl} +template + constexpr T& emplace(Args&&... args) noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_nothrow_constructible_v} is \tcode{true}. + +\pnum +\effects +Equivalent to: +\begin{codeblock} +if (has_value()) { + destroy_at(addressof(@\exposid{val}@)); +} else { + destroy_at(addressof(@\exposid{unex}@)); + @\exposid{has_val}@ = true; +} +return *construct_at(addressof(@\exposid{val}@), std::forward(args)...); +\end{codeblock} +\end{itemdescr} + +\indexlibrarymember{emplace}{expected}% +\begin{itemdecl} +template + constexpr T& emplace(initializer_list il, Args&&... args) noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_nothrow_constructible_v\&, Args...>} +is \tcode{true}. + +\pnum +\effects +Equivalent to: +\begin{codeblock} +if (has_value()) { + destroy_at(addressof(@\exposid{val}@)); +} else { + destroy_at(addressof(@\exposid{unex}@)); + @\exposid{has_val}@ = true; +} +return *construct_at(addressof(@\exposid{val}@), il, std::forward(args)...); +\end{codeblock} +\end{itemdescr} + +\rSec3[expected.object.swap]{Swap} + +\indexlibrarymember{swap}{expected}% +\begin{itemdecl} +constexpr void swap(expected& rhs) noexcept(@\seebelow@); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\begin{itemize} +\item +\tcode{is_swappable_v} is \tcode{true} and +\item +\tcode{is_swappable_v} is \tcode{true} and +\item +\tcode{is_move_constructible_v \&\& is_move_constructible_v} +is \tcode{true}, and +\item +\tcode{is_nothrow_move_constructible_v || is_nothrow_move_constructible_v} +is \tcode{true}. +\end{itemize} + +\pnum +\effects +See \tref{expected.object.swap}. + +\begin{floattable}{\tcode{swap(expected\&)} effects}{expected.object.swap} +{lx{0.35\hsize}x{0.35\hsize}} +\topline +& \chdr{\tcode{this->has_value()}} & \rhdr{\tcode{!this->has_value()}} \\ \capsep +\lhdr{\tcode{rhs.has_value()}} & + equivalent to: \tcode{using std::swap; swap(\exposid{val}, rhs.\exposid{val});} & + calls \tcode{rhs.swap(*this)} \\ +\lhdr{\tcode{!rhs.has_value()}} & + \seebelow & + equivalent to: \tcode{using std::swap; swap(\exposid{unex}, rhs.\exposid{unex});} \\ +\end{floattable} + +For the case where \tcode{rhs.has_value()} is \tcode{false} and +\tcode{this->has_value()} is \tcode{true}, equivalent to: +\begin{codeblock} +if constexpr (is_nothrow_move_constructible_v) { + E tmp(std::move(rhs.@\exposid{unex}@)); + destroy_at(addressof(rhs.@\exposid{unex}@)); + try { + construct_at(addressof(rhs.@\exposid{val}@), std::move(@\exposid{val}@)); + destroy_at(addressof(@\exposid{val}@)); + construct_at(addressof(@\exposid{unex}@), std::move(tmp)); + } catch(...) { + construct_at(addressof(rhs.@\exposid{unex}@), std::move(tmp)); + throw; + } +} else { + remove_cv_t tmp(std::move(@\exposid{val}@)); + destroy_at(addressof(@\exposid{val}@)); + try { + construct_at(addressof(@\exposid{unex}@), std::move(rhs.@\exposid{unex}@)); + destroy_at(addressof(rhs.@\exposid{unex}@)); + construct_at(addressof(rhs.@\exposid{val}@), std::move(tmp)); + } catch (...) { + construct_at(addressof(@\exposid{val}@), std::move(tmp)); + throw; + } +} +@\exposid{has_val}@ = false; +rhs.@\exposid{has_val}@ = true; +\end{codeblock} + +\pnum +\throws +Any exception thrown by the expressions in the \Fundescx{Effects}. + +\pnum +\remarks +The exception specification is equivalent to: +\begin{codeblock} +is_nothrow_move_constructible_v && is_nothrow_swappable_v && +is_nothrow_move_constructible_v && is_nothrow_swappable_v +\end{codeblock} +\end{itemdescr} + +\indexlibrarymember{swap}{expected}% +\begin{itemdecl} +friend constexpr void swap(expected& x, expected& y) noexcept(noexcept(x.swap(y))); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Equivalent to \tcode{x.swap(y)}. +\end{itemdescr} + +\rSec3[expected.object.obs]{Observers} + +\indexlibrarymember{operator->}{expected}% +\begin{itemdecl} +constexpr const T* operator->() const noexcept; +constexpr T* operator->() noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\hardexpects +\tcode{has_value()} is \tcode{true}. + +\pnum +\returns +\tcode{addressof(\exposid{val})}. +\end{itemdescr} + +\indexlibrarymember{operator*}{expected}% +\begin{itemdecl} +constexpr const T& operator*() const & noexcept; +constexpr T& operator*() & noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\hardexpects +\tcode{has_value()} is \tcode{true}. + +\pnum +\returns +\exposid{val}. +\end{itemdescr} + +\indexlibrarymember{operator*}{expected}% +\begin{itemdecl} +constexpr T&& operator*() && noexcept; +constexpr const T&& operator*() const && noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\hardexpects +\tcode{has_value()} is \tcode{true}. + +\pnum +\returns +\tcode{std::move(\exposid{val})}. +\end{itemdescr} + +\indexlibrarymember{operator bool}{expected}% +\indexlibrarymember{has_value}{expected}% +\begin{itemdecl} +constexpr explicit operator bool() const noexcept; +constexpr bool has_value() const noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\exposid{has_val}. +\end{itemdescr} + +\indexlibrarymember{value}{expected}% +\begin{itemdecl} +constexpr const T& value() const &; +constexpr T& value() &; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{is_copy_constructible_v} is \tcode{true}. + +\pnum +\returns +\exposid{val}, if \tcode{has_value()} is \tcode{true}. + +\pnum +\throws +\tcode{bad_expected_access(as_const(error()))} if \tcode{has_value()} is \tcode{false}. +\end{itemdescr} + +\indexlibrarymember{value}{expected}% +\begin{itemdecl} +constexpr T&& value() &&; +constexpr const T&& value() const &&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{is_copy_constructible_v} is \tcode{true} and +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\returns +\tcode{std::move(\exposid{val})}, if \tcode{has_value()} is \tcode{true}. + +\pnum +\throws +\tcode{bad_expected_access(std::move(error()))} +if \tcode{has_value()} is \tcode{false}. +\end{itemdescr} + +\indexlibrarymember{error}{expected}% +\begin{itemdecl} +constexpr const E& error() const & noexcept; +constexpr E& error() & noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\hardexpects +\tcode{has_value()} is \tcode{false}. + +\pnum +\returns +\exposid{unex}. +\end{itemdescr} + +\indexlibrarymember{error}{expected}% +\begin{itemdecl} +constexpr E&& error() && noexcept; +constexpr const E&& error() const && noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\hardexpects +\tcode{has_value()} is \tcode{false}. + +\pnum +\returns +\tcode{std::move(\exposid{unex})}. +\end{itemdescr} + +\indexlibrarymember{value_or}{expected}% +\begin{itemdecl} +template> constexpr T value_or(U&& v) const &; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{is_copy_constructible_v} is \tcode{true} and +\tcode{is_convertible_v} is \tcode{true}. + +\pnum +\returns +\tcode{has_value() ? **this : static_cast(std::forward(v))}. +\end{itemdescr} + +\indexlibrarymember{value_or}{expected}% +\begin{itemdecl} +template> constexpr T value_or(U&& v) &&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{is_move_constructible_v} is \tcode{true} and +\tcode{is_convertible_v} is \tcode{true}. + +\pnum +\returns +\tcode{has_value() ? std::move(**this) : static_cast(std::forward(v))}. +\end{itemdescr} + +\indexlibrarymember{error_or}{expected}% +\begin{itemdecl} +template constexpr E error_or(G&& e) const &; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{is_copy_constructible_v} is \tcode{true} and +\tcode{is_convertible_v} is \tcode{true}. + +\pnum +\returns +\tcode{std::forward(e)} if \tcode{has_value()} is \tcode{true}, +\tcode{error()} otherwise. +\end{itemdescr} + +\indexlibrarymember{error_or}{expected}% +\begin{itemdecl} +template constexpr E error_or(G&& e) &&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{is_move_constructible_v} is \tcode{true} and +\tcode{is_convertible_v} is \tcode{true}. + +\pnum +\returns +\tcode{std::forward(e)} if \tcode{has_value()} is \tcode{true}, +\tcode{std::move(error())} otherwise. +\end{itemdescr} + +\rSec3[expected.object.monadic]{Monadic operations} + +\indexlibrarymember{and_then}{expected}% +\begin{itemdecl} +template constexpr auto and_then(F&& f) &; +template constexpr auto and_then(F&& f) const &; +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{U} be \tcode{remove_cvref_t>}. + +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\mandates +\tcode{U} is a specialization of \tcode{expected} and +\tcode{is_same_v} is \tcode{true}. + +\pnum +\effects +Equivalent to: +\begin{codeblock} +if (has_value()) + return invoke(std::forward(f), @\exposid{val}@); +else + return U(unexpect, error()); +\end{codeblock} +\end{itemdescr} + +\indexlibrarymember{and_then}{expected}% +\begin{itemdecl} +template constexpr auto and_then(F&& f) &&; +template constexpr auto and_then(F&& f) const &&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{U} be +\tcode{remove_cvref_t>}. + +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\mandates +\tcode{U} is a specialization of \tcode{expected} and +\tcode{is_same_v} is \tcode{true}. + +\pnum +\effects +Equivalent to: +\begin{codeblock} +if (has_value()) + return invoke(std::forward(f), std::move(@\exposid{val}@)); +else + return U(unexpect, std::move(error())); +\end{codeblock} +\end{itemdescr} + +\indexlibrarymember{or_else}{expected}% +\begin{itemdecl} +template constexpr auto or_else(F&& f) &; +template constexpr auto or_else(F&& f) const &; +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{G} be \tcode{remove_cvref_t>}. + +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\mandates +\tcode{G} is a specialization of \tcode{expected} and +\tcode{is_same_v} is \tcode{true}. + +\pnum +\effects +Equivalent to: +\begin{codeblock} +if (has_value()) + return G(in_place, @\exposid{val}@); +else + return invoke(std::forward(f), error()); +\end{codeblock} +\end{itemdescr} + +\indexlibrarymember{or_else}{expected}% +\begin{itemdecl} +template constexpr auto or_else(F&& f) &&; +template constexpr auto or_else(F&& f) const &&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{G} be +\tcode{remove_cvref_t>}. + +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\mandates +\tcode{G} is a specialization of \tcode{expected} and +\tcode{is_same_v} is \tcode{true}. + +\pnum +\effects +Equivalent to: +\begin{codeblock} +if (has_value()) + return G(in_place, std::move(@\exposid{val}@)); +else + return invoke(std::forward(f), std::move(error())); +\end{codeblock} +\end{itemdescr} + +\indexlibrarymember{transform}{expected}% +\begin{itemdecl} +template constexpr auto transform(F&& f) &; +template constexpr auto transform(F&& f) const &; +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{U} be +\tcode{remove_cv_t>}. + +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\mandates +\tcode{U} is a valid value type for \tcode{expected}. +If \tcode{is_void_v} is \tcode{false}, +the declaration +\begin{codeblock} +U u(invoke(std::forward(f), @\exposid{val}@)); +\end{codeblock} +is well-formed. + +\pnum +\effects +\begin{itemize} +\item +If \tcode{has_value()} is \tcode{false}, returns +\tcode{expected(unexpect, error())}. +\item +Otherwise, if \tcode{is_void_v} is \tcode{false}, returns an +\tcode{expected} object whose \exposid{has_val} member is \tcode{true} +and \exposid{val} member is direct-non-list-initialized with +\tcode{invoke(std::forward(f), \exposid{val})}. +\item +Otherwise, evaluates \tcode{invoke(std::forward(f), \exposid{val})} and then +returns \tcode{expected()}. +\end{itemize} +\end{itemdescr} + +\indexlibrarymember{transform}{expected}% +\begin{itemdecl} +template constexpr auto transform(F&& f) &&; +template constexpr auto transform(F&& f) const &&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{U} be +\tcode{remove_cv_t>}. + +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\mandates +\tcode{U} is a valid value type for \tcode{expected}. If \tcode{is_void_v} is +\tcode{false}, the declaration +\begin{codeblock} +U u(invoke(std::forward(f), std::move(@\exposid{val}@))); +\end{codeblock} +is well-formed. + +\pnum +\effects +\begin{itemize} +\item +If \tcode{has_value()} is \tcode{false}, returns +\tcode{expected(unexpect, std::move(error()))}. +\item +Otherwise, if \tcode{is_void_v} is \tcode{false}, returns an +\tcode{expected} object whose \exposid{has_val} member is \tcode{true} +and \exposid{val} member is direct-non-list-initialized with +\tcode{invoke(std::forward(f), std::move(\exposid{val}))}. +\item +Otherwise, evaluates \tcode{invoke(std::forward(f), std::move(\exposid{val}))} and +then returns \tcode{ex\-pected()}. +\end{itemize} +\end{itemdescr} + +\indexlibrarymember{transform_error}{expected}% +\begin{itemdecl} +template constexpr auto transform_error(F&& f) &; +template constexpr auto transform_error(F&& f) const &; +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{G} be \tcode{remove_cv_t>}. + +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\mandates +\tcode{G} is a valid template argument +for \tcode{unexpected}\iref{expected.un.general} and the declaration +\begin{codeblock} +G g(invoke(std::forward(f), error())); +\end{codeblock} +is well-formed. + +\pnum +\returns +If \tcode{has_value()} is \tcode{true}, +\tcode{expected(in_place, \exposid{val})}; otherwise, an \tcode{expected} +object whose \exposid{has_val} member is \tcode{false} and \exposid{unex} member +is direct-non-list-initialized with \tcode{invoke(std::forward(f), error())}. +\end{itemdescr} + +\indexlibrarymember{transform_error}{expected}% +\begin{itemdecl} +template constexpr auto transform_error(F&& f) &&; +template constexpr auto transform_error(F&& f) const &&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{G} be +\tcode{remove_cv_t>}. + +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\mandates +\tcode{G} is a valid template argument +for \tcode{unexpected}\iref{expected.un.general} and the declaration +\begin{codeblock} +G g(invoke(std::forward(f), std::move(error()))); +\end{codeblock} +is well-formed. + +\pnum +\returns +If \tcode{has_value()} is \tcode{true}, +\tcode{expected(in_place, std::move(\exposid{val}))}; otherwise, an +\tcode{expected} object whose \exposid{has_val} member is \tcode{false} +and \exposid{unex} member is direct-non-list-initialized with +\tcode{invoke(std::forward(f), std::move(error()))}. +\end{itemdescr} + +\rSec3[expected.object.eq]{Equality operators} + +\indexlibrarymember{operator==}{expected}% +\begin{itemdecl} +template requires (!is_void_v) + friend constexpr bool operator==(const expected& x, const expected& y); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +The expressions \tcode{*x == *y} and \tcode{x.error() == y.error()} +are well-formed and their results are convertible to \tcode{bool}. + +\pnum +\returns +If \tcode{x.has_value()} does not equal \tcode{y.has_value()}, \tcode{false}; +otherwise if \tcode{x.has_value()} is \tcode{true}, \tcode{*x == *y}; +otherwise \tcode{x.error() == y.error()}. +\end{itemdescr} + +\indexlibrarymember{operator==}{expected}% +\begin{itemdecl} +template friend constexpr bool operator==(const expected& x, const T2& v); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{T2} is not a specialization of \tcode{expected}. +The expression \tcode{*x == v} is well-formed and +its result is convertible to \tcode{bool}. +\begin{note} +\tcode{T} need not be \oldconcept{EqualityComparable}. +\end{note} + +\pnum +\returns +If \tcode{x.has_value()} is \tcode{true}, +\tcode{*x == v}; +otherwise \tcode{false}. +\end{itemdescr} + +\indexlibrarymember{operator==}{expected}% +\begin{itemdecl} +template friend constexpr bool operator==(const expected& x, const unexpected& e); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +The expression \tcode{x.error() == e.error()} is well-formed and +its result is convertible to \tcode{bool}. + +\pnum +\returns +If \tcode{!x.has_value()} is \tcode{true}, +\tcode{x.error() == e.error()}; +otherwise \tcode{false}. +\end{itemdescr} + +\rSec2[expected.void]{Partial specialization of \tcode{expected} for \tcode{void} types} + +\rSec3[expected.void.general]{General} + +\begin{codeblock} +template requires is_void_v +class expected { +public: + using @\libmember{value_type}{expected}@ = T; + using @\libmember{error_type}{expected}@ = E; + using @\libmember{unexpected_type}{expected}@ = unexpected; + + template + using @\libmember{rebind}{expected}@ = expected; + + // \ref{expected.void.cons}, constructors + constexpr expected() noexcept; + constexpr expected(const expected&); + constexpr expected(expected&&) noexcept(@\seebelow@); + template + constexpr explicit(@\seebelow@) expected(const expected&); + template + constexpr explicit(@\seebelow@) expected(expected&&); + + template + constexpr explicit(@\seebelow@) expected(const unexpected&); + template + constexpr explicit(@\seebelow@) expected(unexpected&&); + + constexpr explicit expected(in_place_t) noexcept; + template + constexpr explicit expected(unexpect_t, Args&&...); + template + constexpr explicit expected(unexpect_t, initializer_list, Args&&...); + + // \ref{expected.void.dtor}, destructor + constexpr ~expected(); + + // \ref{expected.void.assign}, assignment + constexpr expected& operator=(const expected&); + constexpr expected& operator=(expected&&) noexcept(@\seebelow@); + template + constexpr expected& operator=(const unexpected&); + template + constexpr expected& operator=(unexpected&&); + constexpr void emplace() noexcept; + + // \ref{expected.void.swap}, swap + constexpr void swap(expected&) noexcept(@\seebelow@); + friend constexpr void swap(expected& x, expected& y) noexcept(noexcept(x.swap(y))); + + // \ref{expected.void.obs}, observers + constexpr explicit operator bool() const noexcept; + constexpr bool has_value() const noexcept; + constexpr void operator*() const noexcept; + constexpr void value() const &; // freestanding-deleted + constexpr void value() &&; // freestanding-deleted + constexpr const E& error() const & noexcept; + constexpr E& error() & noexcept; + constexpr const E&& error() const && noexcept; + constexpr E&& error() && noexcept; + template constexpr E error_or(G&&) const &; + template constexpr E error_or(G&&) &&; + + // \ref{expected.void.monadic}, monadic operations + template constexpr auto and_then(F&& f) &; + template constexpr auto and_then(F&& f) &&; + template constexpr auto and_then(F&& f) const &; + template constexpr auto and_then(F&& f) const &&; + template constexpr auto or_else(F&& f) &; + template constexpr auto or_else(F&& f) &&; + template constexpr auto or_else(F&& f) const &; + template constexpr auto or_else(F&& f) const &&; + template constexpr auto transform(F&& f) &; + template constexpr auto transform(F&& f) &&; + template constexpr auto transform(F&& f) const &; + template constexpr auto transform(F&& f) const &&; + template constexpr auto transform_error(F&& f) &; + template constexpr auto transform_error(F&& f) &&; + template constexpr auto transform_error(F&& f) const &; + template constexpr auto transform_error(F&& f) const &&; + + // \ref{expected.void.eq}, equality operators + template requires is_void_v + friend constexpr bool operator==(const expected& x, const expected& y); + template + friend constexpr bool operator==(const expected&, const unexpected&); + +private: + bool @\exposid{has_val}@; // \expos + union { + E @\exposid{unex}@; // \expos + }; +}; +\end{codeblock} + +\pnum +Any object of type \tcode{expected} either +represents a value of type \tcode{T}, or +contains a value of type \tcode{E} +nested within\iref{intro.object} it. +Member \exposid{has_val} indicates whether the \tcode{expected} object +represents a value of type \tcode{T}. + +\pnum +A program that instantiates +the definition of the template \tcode{expected} with +a type for the \tcode{E} parameter that +is not a valid template argument for \tcode{unexpected} is ill-formed. + +\pnum +\tcode{E} shall meet the requirements of +\oldconcept{Destructible} (\tref{cpp17.destructible}). + +\rSec3[expected.void.cons]{Constructors} + +\indexlibraryctor{expected}% +\begin{itemdecl} +constexpr expected() noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\ensures +\tcode{has_value()} is \tcode{true}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +constexpr expected(const expected& rhs); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +If \tcode{rhs.has_value()} is \tcode{false}, +direct-non-list-initializes \exposid{unex} with \tcode{rhs.error()}. + +\pnum +\ensures +\tcode{rhs.has_value() == this->has_value()}. + +\pnum +\throws +Any exception thrown by the initialization of \exposid{unex}. + +\pnum +\remarks +This constructor is defined as deleted +unless \tcode{is_copy_constructible_v} is \tcode{true}. + +\pnum +This constructor is trivial +if \tcode{is_trivially_copy_constructible_v} is \tcode{true}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +constexpr expected(expected&& rhs) noexcept(is_nothrow_move_constructible_v); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_move_constructible_v} is \tcode{true}. + +\pnum +\effects +If \tcode{rhs.has_value()} is \tcode{false}, +direct-non-list-initializes \exposid{unex} with \tcode{std::move(rhs.error())}. + +\pnum +\ensures +\tcode{rhs.has_value()} is unchanged; +\tcode{rhs.has_value() == this->has_value()} is \tcode{true}. + +\pnum +\throws +Any exception thrown by the initialization of \exposid{unex}. + +\pnum +\remarks +This constructor is trivial +if \tcode{is_trivially_move_constructible_v} is \tcode{true}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + constexpr explicit(!is_convertible_v) expected(const expected& rhs); +template + constexpr explicit(!is_convertible_v) expected(expected&& rhs); +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{GF} be \tcode{const G\&} for the first overload and +\tcode{G} for the second overload. + +\pnum +\constraints +\begin{itemize} +\item +\tcode{is_void_v} is \tcode{true}; and +\item +\tcode{is_constructible_v} is \tcode{true}; and +\item +\tcode{is_constructible_v, expected\&>} +is \tcode{false}; and +\item +\tcode{is_constructible_v, expected>} +is \tcode{false}; and +\item +\tcode{is_constructible_v, const expected\&>} +is \tcode{false}; and +\item +\tcode{is_constructible_v, const expected>} +is \tcode{false}. +\end{itemize} + +\pnum +\effects +If \tcode{rhs.has_value()} is \tcode{false}, +direct-non-list-initializes \exposid{unex} +with \tcode{std::forward(rhs.er\-ror())}. + +\pnum +\ensures +\tcode{rhs.has_value()} is unchanged; +\tcode{rhs.has_value() == this->has_value()} is \tcode{true}. + +\pnum +\throws +Any exception thrown by the initialization of \exposid{unex}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + constexpr explicit(!is_convertible_v) expected(const unexpected& e); +template + constexpr explicit(!is_convertible_v) expected(unexpected&& e); +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{GF} be \tcode{const G\&} for the first overload and +\tcode{G} for the second overload. + +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\effects +Direct-non-list-initializes \exposid{unex} +with \tcode{std::forward(e.error())}. + +\pnum +\ensures +\tcode{has_value()} is \tcode{false}. + +\pnum +\throws +Any exception thrown by the initialization of \exposid{unex}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +constexpr explicit expected(in_place_t) noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\ensures +\tcode{has_value()} is \tcode{true}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + constexpr explicit expected(unexpect_t, Args&&... args); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\effects +Direct-non-list-initializes \exposid{unex} +with \tcode{std::forward(args)...}. + +\pnum +\ensures +\tcode{has_value()} is \tcode{false}. + +\pnum +\throws +Any exception thrown by the initialization of \exposid{unex}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + constexpr explicit expected(unexpect_t, initializer_list il, Args&&... args); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v\&, Args...>} is \tcode{true}. + +\pnum +\effects +Direct-non-list-initializes \exposid{unex} +with \tcode{il, std::forward(args)...}. + +\pnum +\ensures +\tcode{has_value()} is \tcode{false}. + +\pnum +\throws +Any exception thrown by the initialization of \exposid{unex}. +\end{itemdescr} + +\rSec3[expected.void.dtor]{Destructor} + +\indexlibrarydtor{expected}% +\begin{itemdecl} +constexpr ~expected(); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +If \tcode{has_value()} is \tcode{false}, destroys \exposid{unex}. + +\pnum +\remarks +If \tcode{is_trivially_destructible_v} is \tcode{true}, +then this destructor is a trivial destructor. +\end{itemdescr} + +\rSec3[expected.void.assign]{Assignment} + +\indexlibrarymember{operator=}{expected}% +\begin{itemdecl} +constexpr expected& operator=(const expected& rhs); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +\begin{itemize} +\item +If \tcode{this->has_value() \&\& rhs.has_value()} is \tcode{true}, no effects. +\item +Otherwise, if \tcode{this->has_value()} is \tcode{true}, +equivalent to: \tcode{construct_at(addressof(\exposid{unex}), rhs.\exposid{unex}); \exposid{has_val} = false;} +\item +Otherwise, if \tcode{rhs.has_value()} is \tcode{true}, +destroys \exposid{unex} and sets \exposid{has_val} to \tcode{true}. +\item +Otherwise, equivalent to \tcode{\exposid{unex} = rhs.error()}. +\end{itemize} + +\pnum +\returns +\tcode{*this}. + +\pnum +\remarks +This operator is defined as deleted unless +\tcode{is_copy_assignable_v} is \tcode{true} and +\tcode{is_copy_constructible_v} is \tcode{true}. + +\pnum +This operator is trivial if +\tcode{is_trivially_copy_constructible_v}, +\tcode{is_trivially_copy_assigna\-ble_v}, and +\tcode{is_trivially_destructible_v} +are all \tcode{true}. +\end{itemdescr} + +\indexlibrarymember{operator=}{expected}% +\begin{itemdecl} +constexpr expected& operator=(expected&& rhs) noexcept(@\seebelow@); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_move_constructible_v} is \tcode{true} and +\tcode{is_move_assignable_v} is \tcode{true}. + +\pnum +\effects +\begin{itemize} +\item +If \tcode{this->has_value() \&\& rhs.has_value()} is \tcode{true}, no effects. +\item +Otherwise, if \tcode{this->has_value()} is \tcode{true}, equivalent to: +\begin{codeblock} +construct_at(addressof(@\exposid{unex}@), std::move(rhs.@\exposid{unex}@)); +@\exposid{has_val}@ = false; +\end{codeblock} +\item +Otherwise, if \tcode{rhs.has_value()} is \tcode{true}, +destroys \exposid{unex} and sets \exposid{has_val} to \tcode{true}. +\item +Otherwise, equivalent to \tcode{\exposid{unex} = std::move(rhs.error())}. +\end{itemize} + +\pnum +\returns +\tcode{*this}. + +\pnum +\remarks +The exception specification is equivalent to +\tcode{is_nothrow_move_constructible_v \&\& is_nothrow_move_assignable_v}. + +\pnum +This operator is trivial if +\tcode{is_trivially_move_constructible_v}, +\tcode{is_trivially_move_assigna\-ble_v}, and +\tcode{is_trivially_destructible_v} +are all \tcode{true}. +\end{itemdescr} + +\indexlibrarymember{operator=}{expected}% +\begin{itemdecl} +template + constexpr expected& operator=(const unexpected& e); +template + constexpr expected& operator=(unexpected&& e); +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{GF} be \tcode{const G\&} for the first overload and +\tcode{G} for the second overload. + +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true} and +\tcode{is_assignable_v} is \tcode{true}. + +\pnum +\effects +\begin{itemize} +\item +If \tcode{has_value()} is \tcode{true}, equivalent to: +\begin{codeblock} +construct_at(addressof(@\exposid{unex}@), std::forward(e.error())); +@\exposid{has_val}@ = false; +\end{codeblock} +\item +Otherwise, equivalent to: +\tcode{\exposid{unex} = std::forward(e.error());} +\end{itemize} + +\pnum +\returns +\tcode{*this}. +\end{itemdescr} + +\indexlibrarymember{emplace}{expected}% +\begin{itemdecl} +constexpr void emplace() noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +If \tcode{has_value()} is \tcode{false}, +destroys \exposid{unex} and sets \exposid{has_val} to \tcode{true}. +\end{itemdescr} + +\rSec3[expected.void.swap]{Swap} + +\indexlibrarymember{swap}{expected}% +\begin{itemdecl} +constexpr void swap(expected& rhs) noexcept(@\seebelow@); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_swappable_v} is \tcode{true} and +\tcode{is_move_constructible_v} is \tcode{true}. + +\pnum +\effects +See \tref{expected.void.swap}. + +\begin{floattable}{\tcode{swap(expected\&)} effects}{expected.void.swap} +{lx{0.35\hsize}x{0.35\hsize}} +\topline +& \chdr{\tcode{this->has_value()}} & \rhdr{\tcode{!this->has_value()}} \\ \capsep +\lhdr{\tcode{rhs.has_value()}} & + no effects & + calls \tcode{rhs.swap(*this)} \\ +\lhdr{\tcode{!rhs.has_value()}} & + \seebelow & + equivalent to: \tcode{using std::swap; swap(\exposid{unex}, rhs.\exposid{unex});} \\ +\end{floattable} + +For the case where \tcode{rhs.has_value()} is \tcode{false} and +\tcode{this->has_value()} is \tcode{true}, equivalent to: +\begin{codeblock} +construct_at(addressof(@\exposid{unex}@), std::move(rhs.@\exposid{unex}@)); +destroy_at(addressof(rhs.@\exposid{unex}@)); +@\exposid{has_val}@ = false; +rhs.@\exposid{has_val}@ = true; +\end{codeblock} + +\pnum +\throws +Any exception thrown by the expressions in the \Fundescx{Effects}. + +\pnum +\remarks +The exception specification is equivalent to +\tcode{is_nothrow_move_constructible_v \&\& is_nothrow_swappable_v}. +\end{itemdescr} + +\indexlibrarymember{swap}{expected}% +\begin{itemdecl} +friend constexpr void swap(expected& x, expected& y) noexcept(noexcept(x.swap(y))); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Equivalent to \tcode{x.swap(y)}. +\end{itemdescr} + +\rSec3[expected.void.obs]{Observers} + +\indexlibrarymember{operator bool}{expected}% +\indexlibrarymember{has_value}{expected}% +\begin{itemdecl} +constexpr explicit operator bool() const noexcept; +constexpr bool has_value() const noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\exposid{has_val}. +\end{itemdescr} + +\indexlibrarymember{operator*}{expected}% +\begin{itemdecl} +constexpr void operator*() const noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\hardexpects +\tcode{has_value()} is \tcode{true}. +\end{itemdescr} + +\indexlibrarymember{value}{expected}% +\begin{itemdecl} +constexpr void value() const &; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{is_copy_constructible_v} is \tcode{true}. + +\pnum +\throws +\tcode{bad_expected_access(error())} if \tcode{has_value()} is \tcode{false}. +\end{itemdescr} + +\indexlibrarymember{value}{expected}% +\begin{itemdecl} +constexpr void value() &&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{is_copy_constructible_v} is \tcode{true} and +\tcode{is_move_constructible_v} is \tcode{true}. + +\pnum +\throws +\tcode{bad_expected_access(std::move(error()))} +if \tcode{has_value()} is \tcode{false}. +\end{itemdescr} + +\indexlibrarymember{error}{expected}% +\begin{itemdecl} +constexpr const E& error() const & noexcept; +constexpr E& error() & noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\hardexpects +\tcode{has_value()} is \tcode{false}. + +\pnum +\returns +\exposid{unex}. +\end{itemdescr} + +\indexlibrarymember{error}{expected}% +\begin{itemdecl} +constexpr E&& error() && noexcept; +constexpr const E&& error() const && noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\hardexpects +\tcode{has_value()} is \tcode{false}. + +\pnum +\returns +\tcode{std::move(\exposid{unex})}. +\end{itemdescr} + +\indexlibrarymember{error_or}{expected}% +\begin{itemdecl} +template constexpr E error_or(G&& e) const &; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{is_copy_constructible_v} is \tcode{true} and +\tcode{is_convertible_v} is \tcode{true}. + +\pnum +\returns +\tcode{std::forward(e)} if \tcode{has_value()} is \tcode{true}, +\tcode{error()} otherwise. +\end{itemdescr} + +\indexlibrarymember{error_or}{expected}% +\begin{itemdecl} +template constexpr E error_or(G&& e) &&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{is_move_constructible_v} is \tcode{true} and +\tcode{is_convertible_v} is \tcode{true}. + +\pnum +\returns +\tcode{std::forward(e)} if \tcode{has_value()} is \tcode{true}, +\tcode{std::move(error())} otherwise. +\end{itemdescr} + +\rSec3[expected.void.monadic]{Monadic operations} + +\indexlibrarymember{and_then}{expected}% +\begin{itemdecl} +template constexpr auto and_then(F&& f) &; +template constexpr auto and_then(F&& f) const &; +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{U} be \tcode{remove_cvref_t>}. + +\pnum +\constraints +\tcode{is_constructible_v>} is \tcode{true}. + +\pnum +\mandates +\tcode{U} is a specialization of \tcode{expected} and +\tcode{is_same_v} is \tcode{true}. + +\pnum +\effects +Equivalent to: +\begin{codeblock} +if (has_value()) + return invoke(std::forward(f)); +else + return U(unexpect, error()); +\end{codeblock} +\end{itemdescr} + +\indexlibrarymember{and_then}{expected}% +\begin{itemdecl} +template constexpr auto and_then(F&& f) &&; +template constexpr auto and_then(F&& f) const &&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{U} be \tcode{remove_cvref_t>}. + +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\mandates +\tcode{U} is a specialization of \tcode{expected} and +\tcode{is_same_v} is \tcode{true}. + +\pnum +\effects +Equivalent to: +\begin{codeblock} +if (has_value()) + return invoke(std::forward(f)); +else + return U(unexpect, std::move(error())); +\end{codeblock} +\end{itemdescr} + +\indexlibrarymember{or_else}{expected}% +\begin{itemdecl} +template constexpr auto or_else(F&& f) &; +template constexpr auto or_else(F&& f) const &; +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{G} be \tcode{remove_cvref_t>}. + +\pnum +\mandates +\tcode{G} is a specialization of \tcode{expected} and +\tcode{is_same_v} is \tcode{true}. + +\pnum +\effects +Equivalent to: +\begin{codeblock} +if (has_value()) + return G(); +else + return invoke(std::forward(f), error()); +\end{codeblock} +\end{itemdescr} + +\indexlibrarymember{or_else}{expected}% +\begin{itemdecl} +template constexpr auto or_else(F&& f) &&; +template constexpr auto or_else(F&& f) const &&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{G} be +\tcode{remove_cvref_t>}. + +\pnum +\mandates +\tcode{G} is a specialization of \tcode{expected} and +\tcode{is_same_v} is \tcode{true}. + +\pnum +\effects +Equivalent to: +\begin{codeblock} +if (has_value()) + return G(); +else + return invoke(std::forward(f), std::move(error())); +\end{codeblock} +\end{itemdescr} + +\indexlibrarymember{transform}{expected}% +\begin{itemdecl} +template constexpr auto transform(F&& f) &; +template constexpr auto transform(F&& f) const &; +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{U} be \tcode{remove_cv_t>}. + +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\mandates +\tcode{U} is a valid value type for \tcode{expected}. If \tcode{is_void_v} is +\tcode{false}, the declaration +\begin{codeblock} +U u(invoke(std::forward(f))); +\end{codeblock} +is well-formed. + +\pnum +\effects +\begin{itemize} +\item +If \tcode{has_value()} is \tcode{false}, returns +\tcode{expected(unexpect, error())}. +\item +Otherwise, if \tcode{is_void_v} is \tcode{false}, returns an +\tcode{expected} object whose \exposid{has_val} member is \tcode{true} and +\exposid{val} member is direct-non-list-initialized with +\tcode{invoke(std::forward(f))}. +\item +Otherwise, evaluates \tcode{invoke(std::forward(f))} and then returns +\tcode{expected()}. +\end{itemize} +\end{itemdescr} + +\indexlibrarymember{transform}{expected}% +\begin{itemdecl} +template constexpr auto transform(F&& f) &&; +template constexpr auto transform(F&& f) const &&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{U} be \tcode{remove_cv_t>}. + +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\mandates +\tcode{U} is a valid value type for \tcode{expected}. If \tcode{is_void_v} is +\tcode{false}, the declaration +\begin{codeblock} +U u(invoke(std::forward(f))); +\end{codeblock} +is well-formed. + +\pnum +\effects +\begin{itemize} +\item +If \tcode{has_value()} is \tcode{false}, returns +\tcode{expected(unexpect, std::move(error()))}. +\item +Otherwise, if \tcode{is_void_v} is \tcode{false}, returns an +\tcode{expected} object whose \exposid{has_val} member is \tcode{true} and +\exposid{val} member is direct-non-list-initialized with +\tcode{invoke(std::forward(f))}. +\item +Otherwise, evaluates \tcode{invoke(std::forward(f))} and then returns +\tcode{expected()}. +\end{itemize} +\end{itemdescr} + +\indexlibrarymember{transform_error}{expected}% +\begin{itemdecl} +template constexpr auto transform_error(F&& f) &; +template constexpr auto transform_error(F&& f) const &; +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{G} be \tcode{remove_cv_t>}. + +\pnum +\mandates +\tcode{G} is a valid template argument +for \tcode{unexpected}\iref{expected.un.general} and the declaration +\begin{codeblock} +G g(invoke(std::forward(f), error())); +\end{codeblock} +is well-formed. + +\pnum +\returns +If \tcode{has_value()} is \tcode{true}, \tcode{expected()}; otherwise, an +\tcode{expected} object whose \exposid{has_val} member is \tcode{false} +and \exposid{unex} member is direct-non-list-initialized with +\tcode{invoke(std::for\-ward(f), error())}. +\end{itemdescr} + +\indexlibrarymember{transform_error}{expected}% +\begin{itemdecl} +template constexpr auto transform_error(F&& f) &&; +template constexpr auto transform_error(F&& f) const &&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{G} be +\tcode{remove_cv_t>}. + +\pnum +\mandates +\tcode{G} is a valid template argument +for \tcode{unexpected}\iref{expected.un.general} and the declaration +\begin{codeblock} +G g(invoke(std::forward(f), std::move(error()))); +\end{codeblock} +is well-formed. + +\pnum +\returns +If \tcode{has_value()} is \tcode{true}, \tcode{expected()}; otherwise, an +\tcode{expected} object whose \exposid{has_val} member is \tcode{false} +and \exposid{unex} member is direct-non-list-initialized with +\tcode{invoke(std::for\-ward(f), std::move(error()))}. +\end{itemdescr} + +\rSec3[expected.void.eq]{Equality operators} + +\indexlibrarymember{operator==}{expected}% +\begin{itemdecl} +template requires is_void_v + friend constexpr bool operator==(const expected& x, const expected& y); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +The expression \tcode{x.error() == y.error()} is well-formed and +its result is convertible to \tcode{bool}. + +\pnum +\returns +If \tcode{x.has_value()} does not equal \tcode{y.has_value()}, \tcode{false}; +otherwise if \tcode{x.has_value()} is \tcode{true}, \tcode{true}; +otherwise \tcode{x.error() == y.error()}. +\end{itemdescr} + +\indexlibrarymember{operator==}{expected}% +\begin{itemdecl} +template + friend constexpr bool operator==(const expected& x, const unexpected& e); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +The expression \tcode{x.error() == e.error()} is well-formed and +its result is convertible to \tcode{bool}. + +\pnum +\returns +If \tcode{!x.has_value()} is \tcode{true}, +\tcode{x.error() == e.error()}; +otherwise \tcode{false}. +\end{itemdescr} + +%%% Local Variables: +%%% mode: LaTeX +%%% TeX-master: t +%%% End: diff --git a/papers/expected-new.tex b/papers/expected-new.tex new file mode 100644 index 0000000..30b9870 --- /dev/null +++ b/papers/expected-new.tex @@ -0,0 +1,3488 @@ +\rSec1[expected]{Expected objects} +\indexlibraryglobal{expected}% + +\rSec2[expected.general]{General} + +\pnum +Subclause \ref{expected} describes the class template \tcode{expected} +that represents expected objects. +An \tcode{expected} object holds +an object of type \tcode{T} or an object of type \tcode{E} and +manages the lifetime of the contained objects. + +\rSec2[expected.syn]{Header \tcode{} synopsis} + +\indexheader{expected}% +\indexlibraryglobal{unexpect_t}% +\indexlibraryglobal{unexpect}% +\begin{codeblock} +// mostly freestanding +namespace std { + // \ref{expected.unexpected}, class template \tcode{unexpected} + template class unexpected; + + // \ref{expected.bad}, class template \tcode{bad_expected_access} + template class bad_expected_access; + + // \ref{expected.bad.void}, specialization for \tcode{void} + template<> class bad_expected_access; + + // in-place construction of unexpected values + struct unexpect_t { + explicit unexpect_t() = default; + }; + inline constexpr unexpect_t unexpect{}; + + // \ref{expected.expected}, class template \tcode{expected} + template class expected; // partially freestanding + + // \ref{expected.void}, partial specialization of \tcode{expected} for \tcode{void} types + template requires is_void_v class expected; // partially freestanding +} +\end{codeblock} + +\rSec2[expected.unexpected]{Class template \tcode{unexpected}} + +\rSec3[expected.un.general]{General} + +\pnum +Subclause \ref{expected.unexpected} describes the class template \tcode{unexpected} +that represents unexpected objects stored in \tcode{expected} objects. + +\indexlibraryglobal{unexpected}% +\begin{codeblock} +namespace std { + template + class unexpected { + public: + // \ref{expected.un.cons}, constructors + constexpr unexpected(const unexpected&) = default; + constexpr unexpected(unexpected&&) = default; + template + constexpr explicit unexpected(Err&&); + template + constexpr explicit unexpected(in_place_t, Args&&...); + template + constexpr explicit unexpected(in_place_t, initializer_list, Args&&...); + + constexpr unexpected& operator=(const unexpected&) = default; + constexpr unexpected& operator=(unexpected&&) = default; + + constexpr const E& error() const & noexcept; + constexpr E& error() & noexcept; + constexpr const E&& error() const && noexcept; + constexpr E&& error() && noexcept; + + constexpr void swap(unexpected& other) noexcept(@\seebelow@); + + template + friend constexpr bool operator==(const unexpected&, const unexpected&); + + friend constexpr void swap(unexpected& x, unexpected& y) noexcept(noexcept(x.swap(y))); + + private: + E @\exposidnc{unex}@; // \expos + }; + + template unexpected(E) -> unexpected; +} +\end{codeblock} + +\pnum +A program that instantiates the definition of \tcode{unexpected} for +a non-object type\added{ other than an lvalue reference type}, +an array type, +a specialization of \tcode{unexpected}, or +a cv-qualified type +is ill-formed. + +\rSec3[expected.un.cons]{Constructors} + +\indexlibraryctor{unexpected}% +\begin{itemdecl} +template + constexpr explicit unexpected(Err&& e); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\begin{itemize} +\item +\tcode{is_same_v, unexpected>} is \tcode{false}; and +\item +\tcode{is_same_v, in_place_t>} is \tcode{false}; and +\item +\tcode{is_constructible_v} is \tcode{true}. +\end{itemize} + +\pnum +\effects +Direct-non-list-initializes \exposid{unex} with \tcode{std::forward(e)}. + +\pnum +\throws +Any exception thrown by the initialization of \exposid{unex}. +\end{itemdescr} + +\indexlibraryctor{unexpected}% +\begin{itemdecl} +template + constexpr explicit unexpected(in_place_t, Args&&... args); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\effects +Direct-non-list-initializes +\exposid{unex} with \tcode{std::forward(args)...}. + +\pnum +\throws +Any exception thrown by the initialization of \exposid{unex}. +\end{itemdescr} + +\indexlibraryctor{unexpected}% +\begin{itemdecl} +template + constexpr explicit unexpected(in_place_t, initializer_list il, Args&&... args); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v\&, Args...>} is \tcode{true}. + +\pnum +\effects +Direct-non-list-initializes +\exposid{unex} with \tcode{il, std::forward(args)...}. + +\pnum +\throws +Any exception thrown by the initialization of \exposid{unex}. +\end{itemdescr} + +\rSec3[expected.un.obs]{Observers} + +\indexlibrarymember{error}{unexpected}% +\begin{itemdecl} +constexpr const E& error() const & noexcept; +constexpr E& error() & noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\exposid{unex}. +\end{itemdescr} + +\indexlibrarymember{error}{unexpected}% +\begin{itemdecl} +constexpr E&& error() && noexcept; +constexpr const E&& error() const && noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\tcode{std::move(\exposid{unex})}. +\end{itemdescr} + +\rSec3[expected.un.swap]{Swap} + +\indexlibrarymember{swap}{unexpected}% +\begin{itemdecl} +constexpr void swap(unexpected& other) noexcept(is_nothrow_swappable_v); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{is_swappable_v} is \tcode{true}. + +\pnum +\effects +Equivalent to: +\tcode{using std::swap; swap(\exposid{unex}, other.\exposid{unex});} +\end{itemdescr} + +\begin{itemdecl} +friend constexpr void swap(unexpected& x, unexpected& y) noexcept(noexcept(x.swap(y))); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_swappable_v} is \tcode{true}. + +\pnum +\effects +Equivalent to \tcode{x.swap(y)}. +\end{itemdescr} + +\rSec3[expected.un.eq]{Equality operator} + +\indexlibrarymember{operator==}{unexpected}% +\begin{itemdecl} +template + friend constexpr bool operator==(const unexpected& x, const unexpected& y); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +The expression \tcode{x.error() == y.error()} is well-formed and +its result is convertible to \tcode{bool}. + +\pnum +\returns +\tcode{x.error() == y.error()}. +\end{itemdescr} + +\begin{addedblock} +\rSec3[expected.un.ref]{Partial specialization \tcode{unexpected}} + +\indexlibraryglobal{unexpected}% +\begin{codeblock} +template +class unexpected { +public: + constexpr unexpected(const unexpected&) = default; + constexpr unexpected(unexpected&&) = default; + template + constexpr explicit unexpected(G&&) noexcept; + template + constexpr explicit unexpected(in_place_t, G&&) noexcept; + + constexpr unexpected& operator=(const unexpected&) = default; + constexpr unexpected& operator=(unexpected&&) = default; + + constexpr E& error() const noexcept; + + constexpr void swap(unexpected& other) noexcept; + + template + friend constexpr bool operator==(const unexpected&, const unexpected&); + + friend constexpr void swap(unexpected& x, unexpected& y) noexcept; + +private: + E* @\exposidnc{unex}@; // \expos +}; +\end{codeblock} + +\pnum +An object of type \tcode{unexpected} holds a pointer to an object of type +\tcode{E}. The referenced object is not owned by the \tcode{unexpected} +object. + +\pnum +A program that instantiates the definition of \tcode{unexpected} for +an array type or a specialization of \tcode{unexpected} is ill-formed. +Unlike the primary template, \tcode{E} may be a cv-qualified type. + +\indexlibraryctor{unexpected}% +\begin{itemdecl} +template + constexpr explicit unexpected(G&& e) noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\begin{itemize} +\item \tcode{is_same_v, unexpected>} is \tcode{false}, +\item \tcode{is_same_v, in_place_t>} is \tcode{false}, +\item \tcode{is_constructible_v} is \tcode{true}, and +\item \tcode{reference_constructs_from_temporary_v} is \tcode{false}. +\end{itemize} + +\pnum +\effects +Initializes \exposid{unex} with +\tcode{addressof(static_cast(std::forward(e)))}. + +\pnum +\remarks +A constructor for which +\tcode{reference_constructs_from_temporary_v} is \tcode{true} --- +one that would bind \tcode{E\&} to a temporary --- is defined as deleted. +\end{itemdescr} + +\indexlibraryctor{unexpected}% +\begin{itemdecl} +template + constexpr explicit unexpected(in_place_t, G&& e) noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true} and +\tcode{reference_constructs_from_temporary_v} is \tcode{false}. + +\pnum +\effects +Initializes \exposid{unex} with +\tcode{addressof(static_cast(std::forward(e)))}. +\end{itemdescr} + +\indexlibrarymember{error}{unexpected}% +\begin{itemdecl} +constexpr E& error() const noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\tcode{*\exposid{unex}}. + +\pnum +\remarks +The reference returned is not \tcode{const}-qualified even when +\tcode{*this} is \tcode{const}; the constness of the \tcode{unexpected} +object does not propagate to the referenced object. +\end{itemdescr} + +\indexlibrarymember{swap}{unexpected}% +\begin{itemdecl} +constexpr void swap(unexpected& other) noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Exchanges \exposid{unex} and \tcode{other.\exposid{unex}}. +\end{itemdescr} + +\indexlibrarymember{operator==}{unexpected}% +\begin{itemdecl} +template + friend constexpr bool operator==(const unexpected& x, const unexpected& y); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +The expression \tcode{x.error() == y.error()} is well-formed and +its result is convertible to \tcode{bool}. + +\pnum +\returns +\tcode{x.error() == y.error()}. +\end{itemdescr} + +\indexlibrarymember{swap}{unexpected}% +\begin{itemdecl} +friend constexpr void swap(unexpected& x, unexpected& y) noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Equivalent to \tcode{x.swap(y)}. +\end{itemdescr} +\end{addedblock} + +\rSec2[expected.bad]{Class template \tcode{bad_expected_access}} + +\indexlibraryglobal{bad_expected_access}% +\begin{codeblock} +namespace std { + template + class bad_expected_access : public bad_expected_access { + public: + constexpr explicit bad_expected_access(E); + constexpr const char* what() const noexcept override; + constexpr E& error() & noexcept; + constexpr const E& error() const & noexcept; + constexpr E&& error() && noexcept; + constexpr const E&& error() const && noexcept; + + private: + E @\exposidnc{unex}@; // \expos + }; +} +\end{codeblock} + +\pnum +The class template \tcode{bad_expected_access} +defines the type of objects thrown as exceptions to report the situation +where an attempt is made to access the value of an \tcode{expected} object +for which \tcode{has_value()} is \tcode{false}. + +\indexlibraryctor{bad_expected_access}% +\begin{itemdecl} +constexpr explicit bad_expected_access(E e); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Initializes \exposid{unex} with \tcode{std::move(e)}. +\end{itemdescr} + +\indexlibrarymember{error}{bad_expected_access}% +\begin{itemdecl} +constexpr const E& error() const & noexcept; +constexpr E& error() & noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\exposid{unex}. +\end{itemdescr} + +\indexlibrarymember{error}{bad_expected_access}% +\begin{itemdecl} +constexpr E&& error() && noexcept; +constexpr const E&& error() const && noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\tcode{std::move(\exposid{unex})}. +\end{itemdescr} + +\indexlibrarymember{what}{bad_expected_access}% +\begin{itemdecl} +constexpr const char* what() const noexcept override; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +An \impldef{return value of \tcode{bad_expected_access::what}} \ntbs, +which during constant evaluation is encoded with +the ordinary literal encoding\iref{lex.ccon}. +\end{itemdescr} + +\rSec2[expected.bad.void]{Class template specialization \tcode{bad_expected_access}} + +\begin{codeblock} +namespace std { + template<> + class bad_expected_access : public exception { + protected: + constexpr bad_expected_access() noexcept; + constexpr bad_expected_access(const bad_expected_access&) noexcept; + constexpr bad_expected_access(bad_expected_access&&) noexcept; + constexpr bad_expected_access& operator=(const bad_expected_access&) noexcept; + constexpr bad_expected_access& operator=(bad_expected_access&&) noexcept; + constexpr ~bad_expected_access(); + + public: + constexpr const char* what() const noexcept override; + }; +} +\end{codeblock} + +\indexlibrarymember{what}{bad_expected_access}% +\begin{itemdecl} +constexpr const char* what() const noexcept override; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +An \impldef{return value of \tcode{bad_expected_access::what}} \ntbs, +which during constant evaluation is encoded with +the ordinary literal encoding\iref{lex.ccon}. +\end{itemdescr} + +\rSec2[expected.expected]{Class template \tcode{expected}} + +\rSec3[expected.object.general]{General} + +\begin{codeblock} +namespace std { + template + class expected { + public: + using @\libmember{value_type}{expected}@ = T; + using @\libmember{error_type}{expected}@ = E; + using @\libmember{unexpected_type}{expected}@ = unexpected; + + template + using @\libmember{rebind}{expected}@ = expected; + + // \ref{expected.object.cons}, constructors + constexpr expected(); + constexpr expected(const expected&); + constexpr expected(expected&&) noexcept(@\seebelow@); + template + constexpr explicit(@\seebelow@) expected(const expected&); + template + constexpr explicit(@\seebelow@) expected(expected&&); + + template> + constexpr explicit(@\seebelow@) expected(U&& v); + + template + constexpr explicit(@\seebelow@) expected(const unexpected&); + template + constexpr explicit(@\seebelow@) expected(unexpected&&); + + template + constexpr explicit expected(in_place_t, Args&&...); + template + constexpr explicit expected(in_place_t, initializer_list, Args&&...); + template + constexpr explicit expected(unexpect_t, Args&&...); + template + constexpr explicit expected(unexpect_t, initializer_list, Args&&...); + + // \ref{expected.object.dtor}, destructor + constexpr ~expected(); + + // \ref{expected.object.assign}, assignment + constexpr expected& operator=(const expected&); + constexpr expected& operator=(expected&&) noexcept(@\seebelow@); + template> constexpr expected& operator=(U&&); + template + constexpr expected& operator=(const unexpected&); + template + constexpr expected& operator=(unexpected&&); + + template + constexpr T& emplace(Args&&...) noexcept; + template + constexpr T& emplace(initializer_list, Args&&...) noexcept; + + // \ref{expected.object.swap}, swap + constexpr void swap(expected&) noexcept(@\seebelow@); + friend constexpr void swap(expected& x, expected& y) noexcept(noexcept(x.swap(y))); + + // \ref{expected.object.obs}, observers + constexpr const T* operator->() const noexcept; + constexpr T* operator->() noexcept; + constexpr const T& operator*() const & noexcept; + constexpr T& operator*() & noexcept; + constexpr const T&& operator*() const && noexcept; + constexpr T&& operator*() && noexcept; + constexpr explicit operator bool() const noexcept; + constexpr bool has_value() const noexcept; + constexpr const T& value() const &; // freestanding-deleted + constexpr T& value() &; // freestanding-deleted + constexpr const T&& value() const &&; // freestanding-deleted + constexpr T&& value() &&; // freestanding-deleted + constexpr const E& error() const & noexcept; + constexpr E& error() & noexcept; + constexpr const E&& error() const && noexcept; + constexpr E&& error() && noexcept; + template> constexpr T value_or(U&&) const &; + template> constexpr T value_or(U&&) &&; + template constexpr E error_or(G&&) const &; + template constexpr E error_or(G&&) &&; + + // \ref{expected.object.monadic}, monadic operations + template constexpr auto and_then(F&& f) &; + template constexpr auto and_then(F&& f) &&; + template constexpr auto and_then(F&& f) const &; + template constexpr auto and_then(F&& f) const &&; + template constexpr auto or_else(F&& f) &; + template constexpr auto or_else(F&& f) &&; + template constexpr auto or_else(F&& f) const &; + template constexpr auto or_else(F&& f) const &&; + template constexpr auto transform(F&& f) &; + template constexpr auto transform(F&& f) &&; + template constexpr auto transform(F&& f) const &; + template constexpr auto transform(F&& f) const &&; + template constexpr auto transform_error(F&& f) &; + template constexpr auto transform_error(F&& f) &&; + template constexpr auto transform_error(F&& f) const &; + template constexpr auto transform_error(F&& f) const &&; + + // \ref{expected.object.eq}, equality operators + template requires (!is_void_v) + friend constexpr bool operator==(const expected& x, const expected& y); + template + friend constexpr bool operator==(const expected&, const T2&); + template + friend constexpr bool operator==(const expected&, const unexpected&); + + private: + bool @\exposid{has_val}@; // \expos + union { + remove_cv_t @\exposid{val}@; // \expos + unexpected @\exposid{unex}@; // \expos + }; + }; +} +\end{codeblock} + +\pnum +Any object of type \tcode{expected} either +contains a value of type \tcode{T} or +a value of type \tcode{E} +nested within\iref{intro.object} it. +Member \exposid{has_val} indicates whether the \tcode{expected} object +contains an object of type \tcode{T}. + +\begin{addedblock} +\pnum +When \tcode{has_value()} is \tcode{false}, the error is +\tcode{\exposid{unex}.error()}. The error is held as an \tcode{unexpected}, +and not as an \tcode{E}, so that \tcode{E} may be an lvalue reference type: an +\tcode{E\&} cannot be a union member, whereas \tcode{unexpected} holds a +pointer to an external object\iref{expected.un.ref}. +\end{addedblock} + +\pnum +A type \tcode{T} is a \term{valid value type for \tcode{expected}}, +if \tcode{remove_cv_t} is \tcode{void} +or a complete non-array object type that is not \tcode{in_place_t}, +\tcode{unexpect_t}, +or a specialization of \tcode{unexpected}. +A program which instantiates class template \tcode{expected} +with an argument \tcode{T} that is not a valid value +type for \tcode{expected} is ill-formed. +A program that instantiates +the definition of the template \tcode{expected} +with a type for the \tcode{E} parameter +that is not a valid template argument for \tcode{unexpected} is ill-formed. + +\pnum +When \tcode{T} is not \cv{} \tcode{void}, it shall meet +the \oldconcept{Destructible} requirements (\tref{cpp17.destructible}). +\added{If \tcode{E} is not an lvalue reference type, }\tcode{E} shall meet +the \oldconcept{Destructible} requirements. + +\rSec3[expected.object.cons]{Constructors} + +\pnum +The exposition-only variable template \exposid{converts-from-any-cvref} +defined in \ref{optional.ctor} +is used by some constructors for \tcode{expected}. + +\indexlibraryctor{expected}% +\begin{itemdecl} +constexpr expected(); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_default_constructible_v} is \tcode{true}. + +\pnum +\effects +Value-initializes \exposid{val}. + +\pnum +\ensures +\tcode{has_value()} is \tcode{true}. + +\pnum +\throws +Any exception thrown by the initialization of \exposid{val}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +constexpr expected(const expected& rhs); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +If \tcode{rhs.has_value()} is \tcode{true}, +direct-non-list-initializes \exposid{val} with \tcode{*rhs}. +Otherwise, direct-non-list-initializes \exposid{unex} with \tcode{rhs.error()}. + +\pnum +\ensures +\tcode{rhs.has_value() == this->has_value()}. + +\pnum +\throws +Any exception thrown by the initialization of \exposid{val} or \exposid{unex}. + +\pnum +\remarks +This constructor is defined as deleted unless +\begin{itemize} +\item +\tcode{is_copy_constructible_v} is \tcode{true} and +\item +\tcode{is_copy_constructible_v} is \tcode{true}. +\end{itemize} + +\pnum +This constructor is trivial if +\begin{itemize} +\item +\tcode{is_trivially_copy_constructible_v} is \tcode{true} and +\item +\tcode{is_trivially_copy_constructible_v} is \tcode{true}. +\end{itemize} +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +constexpr expected(expected&& rhs) noexcept(@\seebelow@); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\begin{itemize} +\item +\tcode{is_move_constructible_v} is \tcode{true} and +\item +\tcode{is_move_constructible_v} is \tcode{true}. +\end{itemize} + +\pnum +\effects +If \tcode{rhs.has_value()} is \tcode{true}, +direct-non-list-initializes \exposid{val} with \tcode{std::move(*rhs)}. +Otherwise, +direct-non-list-initializes \exposid{unex} with \tcode{std::move(rhs.error())}. + +\pnum +\ensures +\tcode{rhs.has_value()} is unchanged; +\tcode{rhs.has_value() == this->has_value()} is \tcode{true}. + +\pnum +\throws +Any exception thrown by the initialization of \exposid{val} or \exposid{unex}. + +\pnum +\remarks +The exception specification is equivalent to +\tcode{is_nothrow_move_constructible_v \&\& +is_nothrow_move_constructible_v}. + +\pnum +This constructor is trivial if +\begin{itemize} +\item +\tcode{is_trivially_move_constructible_v} is \tcode{true} and +\item +\tcode{is_trivially_move_constructible_v} is \tcode{true}. +\end{itemize} +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + constexpr explicit(@\seebelow@) expected(const expected& rhs); +template + constexpr explicit(@\seebelow@) expected(expected&& rhs); +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let: +\begin{itemize} +\item +\tcode{UF} be \tcode{const U\&} for the first overload and +\tcode{U} for the second overload. +\item +\tcode{GF} be \tcode{const G\&} for the first overload and +\tcode{G} for the second overload. +\end{itemize} + +\pnum +\constraints +\begin{itemize} +\item +\tcode{is_constructible_v} is \tcode{true}; and +\item +\tcode{is_constructible_v} is \tcode{true}; and +\item +if \tcode{T} is not \cv{} \tcode{bool}, +\tcode{\exposid{converts-from-any-cvref}>} is \tcode{false}; and +\item +\tcode{is_constructible_v, expected\&>} is \tcode{false}; and +\item +\tcode{is_constructible_v, expected>} is \tcode{false}; and +\item +\tcode{is_constructible_v, const expected\&>} is \tcode{false}; and +\item +\tcode{is_constructible_v, const expected>} is \tcode{false}. +\end{itemize} + +\pnum +\effects +If \tcode{rhs.has_value()}, +direct-non-list-initializes \exposid{val} with \tcode{std::forward(*rhs)}. +Otherwise, +direct-non-list-initializes \exposid{unex} with \tcode{std::forward(rhs.error())}. + +\pnum +\ensures +\tcode{rhs.has_value()} is unchanged; +\tcode{rhs.has_value() == this->has_value()} is \tcode{true}. + +\pnum +\throws +Any exception thrown by the initialization of \exposid{val} or \exposid{unex}. + +\pnum +\remarks +The expression inside \tcode{explicit} is equivalent to +\tcode{!is_convertible_v || !is_convertible_v}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template> + constexpr explicit(!is_convertible_v) expected(U&& v); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\begin{itemize} +\item +\tcode{is_same_v, in_place_t>} is \tcode{false}; and +\item +\tcode{is_same_v, expected>} is \tcode{false}; and +\item +\tcode{is_same_v, unexpect_t>} is \tcode{false}; and +\item +\tcode{remove_cvref_t} is not a specialization of \tcode{unexpected}; and +\item +\tcode{is_constructible_v} is \tcode{true}; and +\item +if \tcode{T} is \cv{} \tcode{bool}, +\tcode{remove_cvref_t} is not a specialization of \tcode{expected}. +\end{itemize} + +\pnum +\effects +Direct-non-list-initializes \exposid{val} with \tcode{std::forward(v)}. + +\pnum +\ensures +\tcode{has_value()} is \tcode{true}. + +\pnum +\throws +Any exception thrown by the initialization of \exposid{val}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + constexpr explicit(!is_convertible_v) expected(const unexpected& e); +template + constexpr explicit(!is_convertible_v) expected(unexpected&& e); +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{GF} be \tcode{const G\&} for the first overload and +\tcode{G} for the second overload. + +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\effects +Direct-non-list-initializes \exposid{unex} with \tcode{std::forward(e.error())}. + +\pnum +\ensures +\tcode{has_value()} is \tcode{false}. + +\pnum +\throws +Any exception thrown by the initialization of \exposid{unex}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + constexpr explicit expected(in_place_t, Args&&... args); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\effects +Direct-non-list-initializes \exposid{val} with \tcode{std::forward(args)...}. + +\pnum +\ensures +\tcode{has_value()} is \tcode{true}. + +\pnum +\throws +Any exception thrown by the initialization of \exposid{val}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + constexpr explicit expected(in_place_t, initializer_list il, Args&&... args); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v\&, Args...>} is \tcode{true}. + +\pnum +\effects +Direct-non-list-initializes \exposid{val} with +\tcode{il, std::forward(args)...}. + +\pnum +\ensures +\tcode{has_value()} is \tcode{true}. + +\pnum +\throws +Any exception thrown by the initialization of \exposid{val}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + constexpr explicit expected(unexpect_t, Args&&... args); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\effects +Direct-non-list-initializes \exposid{unex} with +\tcode{std::forward(args)...}. + +\pnum +\ensures +\tcode{has_value()} is \tcode{false}. + +\pnum +\throws +Any exception thrown by the initialization of \exposid{unex}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + constexpr explicit expected(unexpect_t, initializer_list il, Args&&... args); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v\&, Args...>} is \tcode{true}. + +\pnum +\effects +Direct-non-list-initializes \exposid{unex} with +\tcode{il, std::forward(args)...}. + +\pnum +\ensures +\tcode{has_value()} is \tcode{false}. + +\pnum +\throws +Any exception thrown by the initialization of \exposid{unex}. +\end{itemdescr} + +\rSec3[expected.object.dtor]{Destructor} + +\indexlibrarydtor{expected}% +\begin{itemdecl} +constexpr ~expected(); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +If \tcode{has_value()} is \tcode{true}, destroys \exposid{val}, +otherwise destroys \exposid{unex}. + +\pnum +\remarks +If \tcode{is_trivially_destructible_v} is \tcode{true}, and +\tcode{is_trivially_destructible_v} is \tcode{true}, +then this destructor is a trivial destructor. +\end{itemdescr} + +\rSec3[expected.object.assign]{Assignment} + +\pnum +This subclause makes use of the following exposition-only function template: +\begin{codeblock} +template +constexpr void @\exposid{reinit-expected}@(T& newval, U& oldval, Args&&... args) { // \expos + if constexpr (is_nothrow_constructible_v) { + destroy_at(addressof(oldval)); + construct_at(addressof(newval), std::forward(args)...); + } else if constexpr (is_nothrow_move_constructible_v) { + T tmp(std::forward(args)...); + destroy_at(addressof(oldval)); + construct_at(addressof(newval), std::move(tmp)); + } else { + U tmp(std::move(oldval)); + destroy_at(addressof(oldval)); + try { + construct_at(addressof(newval), std::forward(args)...); + } catch (...) { + construct_at(addressof(oldval), std::move(tmp)); + throw; + } + } +} +\end{codeblock} + +\indexlibrarymember{operator=}{expected}% +\begin{itemdecl} +constexpr expected& operator=(const expected& rhs); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +\begin{itemize} +\item +If \tcode{this->has_value() \&\& rhs.has_value()} is \tcode{true}, +equivalent to \tcode{\exposid{val} = *rhs}. +\item +Otherwise, if \tcode{this->has_value()} is \tcode{true}, equivalent to: +\begin{codeblock} +@\exposid{reinit-expected}@(@\exposid{unex}@, @\exposid{val}@, rhs.error()) +\end{codeblock} +\item +Otherwise, if \tcode{rhs.has_value()} is \tcode{true}, equivalent to: +\begin{codeblock} +@\exposid{reinit-expected}@(@\exposid{val}@, @\exposid{unex}@, *rhs) +\end{codeblock} +\item +Otherwise, equivalent to \tcode{\exposid{unex} = rhs.\exposid{unex}}. +\end{itemize} +Then, if no exception was thrown, +equivalent to: \tcode{\exposid{has_val} = rhs.has_value(); return *this;} + +\pnum +\returns +\tcode{*this}. + +\pnum +\remarks +This operator is defined as deleted unless: +\begin{itemize} +\item +\tcode{is_copy_assignable_v} is \tcode{true} and +\item +\tcode{is_copy_constructible_v} is \tcode{true} and +\item +\tcode{is_copy_assignable_v} is \tcode{true} and +\item +\tcode{is_copy_constructible_v} is \tcode{true} and +\item +\tcode{is_nothrow_move_constructible_v || is_nothrow_move_constructible_v} +is \tcode{true}. +\end{itemize} + +\pnum +This operator is trivial if: +\begin{itemize} +\item \tcode{is_trivially_copy_constructible_v} is \tcode{true}, and +\item \tcode{is_trivially_copy_assignable_v} is \tcode{true}, and +\item \tcode{is_trivially_destructible_v} is \tcode{true}, and +\item \tcode{is_trivially_copy_constructible_v} is \tcode{true}, and +\item \tcode{is_trivially_copy_assignable_v} is \tcode{true}, and +\item \tcode{is_trivially_destructible_v} is \tcode{true}. +\end{itemize} +\end{itemdescr} + +\indexlibrarymember{operator=}{expected}% +\begin{itemdecl} +constexpr expected& operator=(expected&& rhs) noexcept(@\seebelow@); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\begin{itemize} +\item +\tcode{is_move_constructible_v} is \tcode{true} and +\item +\tcode{is_move_assignable_v} is \tcode{true} and +\item +\tcode{is_move_constructible_v} is \tcode{true} and +\item +\tcode{is_move_assignable_v} is \tcode{true} and +\item +\tcode{is_nothrow_move_constructible_v || is_nothrow_move_constructible_v} +is \tcode{true}. +\end{itemize} + +\pnum +\effects +\begin{itemize} +\item +If \tcode{this->has_value() \&\& rhs.has_value()} is \tcode{true}, +equivalent to \tcode{\exposid{val} = std::move(*rhs)}. +\item +Otherwise, if \tcode{this->has_value()} is \tcode{true}, equivalent to: +\begin{codeblock} +@\exposid{reinit-expected}@(@\exposid{unex}@, @\exposid{val}@, std::move(rhs.error())) +\end{codeblock} +\item +Otherwise, if \tcode{rhs.has_value()} is \tcode{true}, equivalent to: +\begin{codeblock} +@\exposid{reinit-expected}@(@\exposid{val}@, @\exposid{unex}@, std::move(*rhs)) +\end{codeblock} +\item +Otherwise, equivalent to \tcode{\exposid{unex} = std::move(rhs.\exposid{unex})}. +\end{itemize} +Then, if no exception was thrown, +equivalent to: \tcode{has_val = rhs.has_value(); return *this;} + +\pnum +\returns +\tcode{*this}. + +\pnum +\remarks +The exception specification is equivalent to: +\begin{codeblock} +is_nothrow_move_assignable_v && is_nothrow_move_constructible_v && +is_nothrow_move_assignable_v && is_nothrow_move_constructible_v +\end{codeblock} + +\pnum +This operator is trivial if: +\begin{itemize} +\item \tcode{is_trivially_move_constructible_v} is \tcode{true}, and +\item \tcode{is_trivially_move_assignable_v} is \tcode{true}, and +\item \tcode{is_trivially_destructible_v} is \tcode{true}, and +\item \tcode{is_trivially_move_constructible_v} is \tcode{true}, and +\item \tcode{is_trivially_move_assignable_v} is \tcode{true}, and +\item \tcode{is_trivially_destructible_v} is \tcode{true}. +\end{itemize} +\end{itemdescr} + +\indexlibrarymember{operator=}{expected}% +\begin{itemdecl} +template> + constexpr expected& operator=(U&& v); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\begin{itemize} +\item +\tcode{is_same_v>} is \tcode{false}; and +\item +\tcode{remove_cvref_t} is not a specialization of \tcode{unexpected}; and +\item +\tcode{is_constructible_v} is \tcode{true}; and +\item +\tcode{is_assignable_v} is \tcode{true}; and +\item +\tcode{is_nothrow_constructible_v || is_nothrow_move_constructible_v ||\newline +is_nothrow_move_constructible_v} +is \tcode{true}. +\end{itemize} + +\pnum +\effects +\begin{itemize} +\item +If \tcode{has_value()} is \tcode{true}, +equivalent to: \tcode{\exposid{val} = std::forward(v);} +\item +Otherwise, equivalent to: +\begin{codeblock} +@\exposid{reinit-expected}@(@\exposid{val}@, @\exposid{unex}@, std::forward(v)); +@\exposid{has_val}@ = true; +\end{codeblock} +\end{itemize} + +\pnum +\returns +\tcode{*this}. +\end{itemdescr} + +\indexlibrarymember{operator=}{expected}% +\begin{itemdecl} +template + constexpr expected& operator=(const unexpected& e); +template + constexpr expected& operator=(unexpected&& e); +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{GF} be \tcode{const G\&} for the first overload and +\tcode{G} for the second overload. + +\pnum +\constraints +\begin{itemize} +\item +\tcode{is_constructible_v} is \tcode{true}; and +\item +\tcode{is_assignable_v} is \tcode{true}; and +\item +\tcode{is_nothrow_constructible_v || is_nothrow_move_constructible_v ||\newline +is_nothrow_move_constructible_v} is \tcode{true}. +\end{itemize} + +\pnum +\effects +\begin{itemize} +\item +If \tcode{has_value()} is \tcode{true}, equivalent to: +\begin{codeblock} +@\exposid{reinit-expected}@(@\exposid{unex}@, @\exposid{val}@, std::forward(e.error())); +@\exposid{has_val}@ = false; +\end{codeblock} +\item +Otherwise, equivalent to: +\tcode{\exposid{unex} = unexpected(std::forward(e.error()));} +\end{itemize} + +\pnum +\returns +\tcode{*this}. +\end{itemdescr} + +\indexlibrarymember{emplace}{expected}% +\begin{itemdecl} +template + constexpr T& emplace(Args&&... args) noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_nothrow_constructible_v} is \tcode{true}. + +\pnum +\effects +Equivalent to: +\begin{codeblock} +if (has_value()) { + destroy_at(addressof(@\exposid{val}@)); +} else { + destroy_at(addressof(@\exposid{unex}@)); + @\exposid{has_val}@ = true; +} +return *construct_at(addressof(@\exposid{val}@), std::forward(args)...); +\end{codeblock} +\end{itemdescr} + +\indexlibrarymember{emplace}{expected}% +\begin{itemdecl} +template + constexpr T& emplace(initializer_list il, Args&&... args) noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_nothrow_constructible_v\&, Args...>} +is \tcode{true}. + +\pnum +\effects +Equivalent to: +\begin{codeblock} +if (has_value()) { + destroy_at(addressof(@\exposid{val}@)); +} else { + destroy_at(addressof(@\exposid{unex}@)); + @\exposid{has_val}@ = true; +} +return *construct_at(addressof(@\exposid{val}@), il, std::forward(args)...); +\end{codeblock} +\end{itemdescr} + +\rSec3[expected.object.swap]{Swap} + +\indexlibrarymember{swap}{expected}% +\begin{itemdecl} +constexpr void swap(expected& rhs) noexcept(@\seebelow@); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\begin{itemize} +\item +\tcode{is_swappable_v} is \tcode{true} and +\item +\tcode{is_swappable_v} is \tcode{true} and +\item +\tcode{is_move_constructible_v \&\& is_move_constructible_v} +is \tcode{true}, and +\item +\tcode{is_nothrow_move_constructible_v || is_nothrow_move_constructible_v} +is \tcode{true}. +\end{itemize} + +\pnum +\effects +See \tref{expected.object.swap}. + +\begin{floattable}{\tcode{swap(expected\&)} effects}{expected.object.swap} +{lx{0.35\hsize}x{0.35\hsize}} +\topline +& \chdr{\tcode{this->has_value()}} & \rhdr{\tcode{!this->has_value()}} \\ \capsep +\lhdr{\tcode{rhs.has_value()}} & + equivalent to: \tcode{using std::swap; swap(\exposid{val}, rhs.\exposid{val});} & + calls \tcode{rhs.swap(*this)} \\ +\lhdr{\tcode{!rhs.has_value()}} & + \seebelow & + equivalent to: \tcode{using std::swap; swap(\exposid{unex}, rhs.\exposid{unex});} \\ +\end{floattable} + +For the case where \tcode{rhs.has_value()} is \tcode{false} and +\tcode{this->has_value()} is \tcode{true}, equivalent to: +\begin{codeblock} +if constexpr (is_nothrow_move_constructible_v>) { + unexpected tmp(std::move(rhs.@\exposid{unex}@)); + destroy_at(addressof(rhs.@\exposid{unex}@)); + try { + construct_at(addressof(rhs.@\exposid{val}@), std::move(@\exposid{val}@)); + destroy_at(addressof(@\exposid{val}@)); + construct_at(addressof(@\exposid{unex}@), std::move(tmp)); + } catch(...) { + construct_at(addressof(rhs.@\exposid{unex}@), std::move(tmp)); + throw; + } +} else { + remove_cv_t tmp(std::move(@\exposid{val}@)); + destroy_at(addressof(@\exposid{val}@)); + try { + construct_at(addressof(@\exposid{unex}@), std::move(rhs.@\exposid{unex}@)); + destroy_at(addressof(rhs.@\exposid{unex}@)); + construct_at(addressof(rhs.@\exposid{val}@), std::move(tmp)); + } catch (...) { + construct_at(addressof(@\exposid{val}@), std::move(tmp)); + throw; + } +} +@\exposid{has_val}@ = false; +rhs.@\exposid{has_val}@ = true; +\end{codeblock} + +\pnum +\throws +Any exception thrown by the expressions in the \Fundescx{Effects}. + +\pnum +\remarks +The exception specification is equivalent to: +\begin{codeblock} +is_nothrow_move_constructible_v && is_nothrow_swappable_v && +is_nothrow_move_constructible_v> && is_nothrow_swappable_v> +\end{codeblock} +\end{itemdescr} + +\indexlibrarymember{swap}{expected}% +\begin{itemdecl} +friend constexpr void swap(expected& x, expected& y) noexcept(noexcept(x.swap(y))); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Equivalent to \tcode{x.swap(y)}. +\end{itemdescr} + +\rSec3[expected.object.obs]{Observers} + +\indexlibrarymember{operator->}{expected}% +\begin{itemdecl} +constexpr const T* operator->() const noexcept; +constexpr T* operator->() noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\hardexpects +\tcode{has_value()} is \tcode{true}. + +\pnum +\returns +\tcode{addressof(\exposid{val})}. +\end{itemdescr} + +\indexlibrarymember{operator*}{expected}% +\begin{itemdecl} +constexpr const T& operator*() const & noexcept; +constexpr T& operator*() & noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\hardexpects +\tcode{has_value()} is \tcode{true}. + +\pnum +\returns +\exposid{val}. +\end{itemdescr} + +\indexlibrarymember{operator*}{expected}% +\begin{itemdecl} +constexpr T&& operator*() && noexcept; +constexpr const T&& operator*() const && noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\hardexpects +\tcode{has_value()} is \tcode{true}. + +\pnum +\returns +\tcode{std::move(\exposid{val})}. +\end{itemdescr} + +\indexlibrarymember{operator bool}{expected}% +\indexlibrarymember{has_value}{expected}% +\begin{itemdecl} +constexpr explicit operator bool() const noexcept; +constexpr bool has_value() const noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\exposid{has_val}. +\end{itemdescr} + +\indexlibrarymember{value}{expected}% +\begin{itemdecl} +constexpr const T& value() const &; +constexpr T& value() &; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{is_copy_constructible_v} is \tcode{true}. + +\pnum +\returns +\exposid{val}, if \tcode{has_value()} is \tcode{true}. + +\pnum +\throws +\tcode{bad_expected_access(as_const(error()))} if \tcode{has_value()} is \tcode{false}. +\end{itemdescr} + +\indexlibrarymember{value}{expected}% +\begin{itemdecl} +constexpr T&& value() &&; +constexpr const T&& value() const &&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{is_copy_constructible_v} is \tcode{true} and +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\returns +\tcode{std::move(\exposid{val})}, if \tcode{has_value()} is \tcode{true}. + +\pnum +\throws +\tcode{bad_expected_access(std::move(error()))} +if \tcode{has_value()} is \tcode{false}. +\end{itemdescr} + +\indexlibrarymember{error}{expected}% +\begin{itemdecl} +constexpr const E& error() const & noexcept; +constexpr E& error() & noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\hardexpects +\tcode{has_value()} is \tcode{false}. + +\pnum +\returns +\tcode{\exposid{unex}.error()}. +\end{itemdescr} + +\indexlibrarymember{error}{expected}% +\begin{itemdecl} +constexpr E&& error() && noexcept; +constexpr const E&& error() const && noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\hardexpects +\tcode{has_value()} is \tcode{false}. + +\pnum +\returns +\tcode{std::move(\exposid{unex}).error()}. +\end{itemdescr} + +\indexlibrarymember{value_or}{expected}% +\begin{itemdecl} +template> constexpr T value_or(U&& v) const &; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{is_copy_constructible_v} is \tcode{true} and +\tcode{is_convertible_v} is \tcode{true}. + +\pnum +\returns +\tcode{has_value() ? **this : static_cast(std::forward(v))}. +\end{itemdescr} + +\indexlibrarymember{value_or}{expected}% +\begin{itemdecl} +template> constexpr T value_or(U&& v) &&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{is_move_constructible_v} is \tcode{true} and +\tcode{is_convertible_v} is \tcode{true}. + +\pnum +\returns +\tcode{has_value() ? std::move(**this) : static_cast(std::forward(v))}. +\end{itemdescr} + +\indexlibrarymember{error_or}{expected}% +\begin{itemdecl} +template constexpr E error_or(G&& e) const &; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{is_copy_constructible_v} is \tcode{true} and +\tcode{is_convertible_v} is \tcode{true}. + +\pnum +\returns +\tcode{std::forward(e)} if \tcode{has_value()} is \tcode{true}, +\tcode{error()} otherwise. +\end{itemdescr} + +\indexlibrarymember{error_or}{expected}% +\begin{itemdecl} +template constexpr E error_or(G&& e) &&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{is_move_constructible_v} is \tcode{true} and +\tcode{is_convertible_v} is \tcode{true}. + +\pnum +\returns +\tcode{std::forward(e)} if \tcode{has_value()} is \tcode{true}, +\tcode{std::move(error())} otherwise. +\end{itemdescr} + +\rSec3[expected.object.monadic]{Monadic operations} + +\indexlibrarymember{and_then}{expected}% +\begin{itemdecl} +template constexpr auto and_then(F&& f) &; +template constexpr auto and_then(F&& f) const &; +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{U} be \tcode{remove_cvref_t>}. + +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\mandates +\tcode{U} is a specialization of \tcode{expected} and +\tcode{is_same_v} is \tcode{true}. + +\pnum +\effects +Equivalent to: +\begin{codeblock} +if (has_value()) + return invoke(std::forward(f), @\exposid{val}@); +else + return U(unexpect, error()); +\end{codeblock} +\end{itemdescr} + +\indexlibrarymember{and_then}{expected}% +\begin{itemdecl} +template constexpr auto and_then(F&& f) &&; +template constexpr auto and_then(F&& f) const &&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{U} be +\tcode{remove_cvref_t>}. + +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\mandates +\tcode{U} is a specialization of \tcode{expected} and +\tcode{is_same_v} is \tcode{true}. + +\pnum +\effects +Equivalent to: +\begin{codeblock} +if (has_value()) + return invoke(std::forward(f), std::move(@\exposid{val}@)); +else + return U(unexpect, std::move(error())); +\end{codeblock} +\end{itemdescr} + +\indexlibrarymember{or_else}{expected}% +\begin{itemdecl} +template constexpr auto or_else(F&& f) &; +template constexpr auto or_else(F&& f) const &; +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{G} be \tcode{remove_cvref_t>}. + +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\mandates +\tcode{G} is a specialization of \tcode{expected} and +\tcode{is_same_v} is \tcode{true}. + +\pnum +\effects +Equivalent to: +\begin{codeblock} +if (has_value()) + return G(in_place, @\exposid{val}@); +else + return invoke(std::forward(f), error()); +\end{codeblock} +\end{itemdescr} + +\indexlibrarymember{or_else}{expected}% +\begin{itemdecl} +template constexpr auto or_else(F&& f) &&; +template constexpr auto or_else(F&& f) const &&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{G} be +\tcode{remove_cvref_t>}. + +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\mandates +\tcode{G} is a specialization of \tcode{expected} and +\tcode{is_same_v} is \tcode{true}. + +\pnum +\effects +Equivalent to: +\begin{codeblock} +if (has_value()) + return G(in_place, std::move(@\exposid{val}@)); +else + return invoke(std::forward(f), std::move(error())); +\end{codeblock} +\end{itemdescr} + +\indexlibrarymember{transform}{expected}% +\begin{itemdecl} +template constexpr auto transform(F&& f) &; +template constexpr auto transform(F&& f) const &; +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{U} be +\tcode{remove_cv_t>}. + +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\mandates +\tcode{U} is a valid value type for \tcode{expected}. +If \tcode{is_void_v} is \tcode{false}, +the declaration +\begin{codeblock} +U u(invoke(std::forward(f), @\exposid{val}@)); +\end{codeblock} +is well-formed. + +\pnum +\effects +\begin{itemize} +\item +If \tcode{has_value()} is \tcode{false}, returns +\tcode{expected(unexpect, error())}. +\item +Otherwise, if \tcode{is_void_v} is \tcode{false}, returns an +\tcode{expected} object whose \exposid{has_val} member is \tcode{true} +and \exposid{val} member is direct-non-list-initialized with +\tcode{invoke(std::forward(f), \exposid{val})}. +\item +Otherwise, evaluates \tcode{invoke(std::forward(f), \exposid{val})} and then +returns \tcode{expected()}. +\end{itemize} +\end{itemdescr} + +\indexlibrarymember{transform}{expected}% +\begin{itemdecl} +template constexpr auto transform(F&& f) &&; +template constexpr auto transform(F&& f) const &&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{U} be +\tcode{remove_cv_t>}. + +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\mandates +\tcode{U} is a valid value type for \tcode{expected}. If \tcode{is_void_v} is +\tcode{false}, the declaration +\begin{codeblock} +U u(invoke(std::forward(f), std::move(@\exposid{val}@))); +\end{codeblock} +is well-formed. + +\pnum +\effects +\begin{itemize} +\item +If \tcode{has_value()} is \tcode{false}, returns +\tcode{expected(unexpect, std::move(error()))}. +\item +Otherwise, if \tcode{is_void_v} is \tcode{false}, returns an +\tcode{expected} object whose \exposid{has_val} member is \tcode{true} +and \exposid{val} member is direct-non-list-initialized with +\tcode{invoke(std::forward(f), std::move(\exposid{val}))}. +\item +Otherwise, evaluates \tcode{invoke(std::forward(f), std::move(\exposid{val}))} and +then returns \tcode{ex\-pected()}. +\end{itemize} +\end{itemdescr} + +\indexlibrarymember{transform_error}{expected}% +\begin{itemdecl} +template constexpr auto transform_error(F&& f) &; +template constexpr auto transform_error(F&& f) const &; +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{G} be \tcode{remove_cv_t>}. + +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\mandates +\tcode{G} is a valid template argument +for \tcode{unexpected}\iref{expected.un.general} and the declaration +\begin{codeblock} +G g(invoke(std::forward(f), error())); +\end{codeblock} +is well-formed. + +\pnum +\returns +If \tcode{has_value()} is \tcode{true}, +\tcode{expected(in_place, \exposid{val})}; otherwise, an \tcode{expected} +object whose \exposid{has_val} member is \tcode{false} and \exposid{unex} member +is direct-non-list-initialized with \tcode{invoke(std::forward(f), error())}. +\end{itemdescr} + +\indexlibrarymember{transform_error}{expected}% +\begin{itemdecl} +template constexpr auto transform_error(F&& f) &&; +template constexpr auto transform_error(F&& f) const &&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{G} be +\tcode{remove_cv_t>}. + +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\mandates +\tcode{G} is a valid template argument +for \tcode{unexpected}\iref{expected.un.general} and the declaration +\begin{codeblock} +G g(invoke(std::forward(f), std::move(error()))); +\end{codeblock} +is well-formed. + +\pnum +\returns +If \tcode{has_value()} is \tcode{true}, +\tcode{expected(in_place, std::move(\exposid{val}))}; otherwise, an +\tcode{expected} object whose \exposid{has_val} member is \tcode{false} +and \exposid{unex} member is direct-non-list-initialized with +\tcode{invoke(std::forward(f), std::move(error()))}. +\end{itemdescr} + +\rSec3[expected.object.eq]{Equality operators} + +\indexlibrarymember{operator==}{expected}% +\begin{itemdecl} +template requires (!is_void_v) + friend constexpr bool operator==(const expected& x, const expected& y); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +The expressions \tcode{*x == *y} and \tcode{x.error() == y.error()} +are well-formed and their results are convertible to \tcode{bool}. + +\pnum +\returns +If \tcode{x.has_value()} does not equal \tcode{y.has_value()}, \tcode{false}; +otherwise if \tcode{x.has_value()} is \tcode{true}, \tcode{*x == *y}; +otherwise \tcode{x.error() == y.error()}. +\end{itemdescr} + +\indexlibrarymember{operator==}{expected}% +\begin{itemdecl} +template friend constexpr bool operator==(const expected& x, const T2& v); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{T2} is not a specialization of \tcode{expected}. +The expression \tcode{*x == v} is well-formed and +its result is convertible to \tcode{bool}. +\begin{note} +\tcode{T} need not be \oldconcept{EqualityComparable}. +\end{note} + +\pnum +\returns +If \tcode{x.has_value()} is \tcode{true}, +\tcode{*x == v}; +otherwise \tcode{false}. +\end{itemdescr} + +\indexlibrarymember{operator==}{expected}% +\begin{itemdecl} +template friend constexpr bool operator==(const expected& x, const unexpected& e); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +The expression \tcode{x.error() == e.error()} is well-formed and +its result is convertible to \tcode{bool}. + +\pnum +\returns +If \tcode{!x.has_value()} is \tcode{true}, +\tcode{x.error() == e.error()}; +otherwise \tcode{false}. +\end{itemdescr} + +\rSec2[expected.void]{Partial specialization of \tcode{expected} for \tcode{void} types} + +\rSec3[expected.void.general]{General} + +\begin{codeblock} +template requires is_void_v +class expected { +public: + using @\libmember{value_type}{expected}@ = T; + using @\libmember{error_type}{expected}@ = E; + using @\libmember{unexpected_type}{expected}@ = unexpected; + + template + using @\libmember{rebind}{expected}@ = expected; + + // \ref{expected.void.cons}, constructors + constexpr expected() noexcept; + constexpr expected(const expected&); + constexpr expected(expected&&) noexcept(@\seebelow@); + template + constexpr explicit(@\seebelow@) expected(const expected&); + template + constexpr explicit(@\seebelow@) expected(expected&&); + + template + constexpr explicit(@\seebelow@) expected(const unexpected&); + template + constexpr explicit(@\seebelow@) expected(unexpected&&); + + constexpr explicit expected(in_place_t) noexcept; + template + constexpr explicit expected(unexpect_t, Args&&...); + template + constexpr explicit expected(unexpect_t, initializer_list, Args&&...); + + // \ref{expected.void.dtor}, destructor + constexpr ~expected(); + + // \ref{expected.void.assign}, assignment + constexpr expected& operator=(const expected&); + constexpr expected& operator=(expected&&) noexcept(@\seebelow@); + template + constexpr expected& operator=(const unexpected&); + template + constexpr expected& operator=(unexpected&&); + constexpr void emplace() noexcept; + + // \ref{expected.void.swap}, swap + constexpr void swap(expected&) noexcept(@\seebelow@); + friend constexpr void swap(expected& x, expected& y) noexcept(noexcept(x.swap(y))); + + // \ref{expected.void.obs}, observers + constexpr explicit operator bool() const noexcept; + constexpr bool has_value() const noexcept; + constexpr void operator*() const noexcept; + constexpr void value() const &; // freestanding-deleted + constexpr void value() &&; // freestanding-deleted + constexpr const E& error() const & noexcept; + constexpr E& error() & noexcept; + constexpr const E&& error() const && noexcept; + constexpr E&& error() && noexcept; + template constexpr E error_or(G&&) const &; + template constexpr E error_or(G&&) &&; + + // \ref{expected.void.monadic}, monadic operations + template constexpr auto and_then(F&& f) &; + template constexpr auto and_then(F&& f) &&; + template constexpr auto and_then(F&& f) const &; + template constexpr auto and_then(F&& f) const &&; + template constexpr auto or_else(F&& f) &; + template constexpr auto or_else(F&& f) &&; + template constexpr auto or_else(F&& f) const &; + template constexpr auto or_else(F&& f) const &&; + template constexpr auto transform(F&& f) &; + template constexpr auto transform(F&& f) &&; + template constexpr auto transform(F&& f) const &; + template constexpr auto transform(F&& f) const &&; + template constexpr auto transform_error(F&& f) &; + template constexpr auto transform_error(F&& f) &&; + template constexpr auto transform_error(F&& f) const &; + template constexpr auto transform_error(F&& f) const &&; + + // \ref{expected.void.eq}, equality operators + template requires is_void_v + friend constexpr bool operator==(const expected& x, const expected& y); + template + friend constexpr bool operator==(const expected&, const unexpected&); + +private: + bool @\exposid{has_val}@; // \expos + union { + unexpected @\exposid{unex}@; // \expos + }; +}; +\end{codeblock} + +\pnum +Any object of type \tcode{expected} either +represents a value of type \tcode{T}, or +contains a value of type \tcode{E} +nested within\iref{intro.object} it. +Member \exposid{has_val} indicates whether the \tcode{expected} object +represents a value of type \tcode{T}. + +\begin{addedblock} +\pnum +When \tcode{has_value()} is \tcode{false}, the error is +\tcode{\exposid{unex}.error()}. The error is held as an \tcode{unexpected}, +and not as an \tcode{E}, so that \tcode{E} may be an lvalue reference type: an +\tcode{E\&} cannot be a union member, whereas \tcode{unexpected} holds a +pointer to an external object\iref{expected.un.ref}. +\end{addedblock} + +\pnum +A program that instantiates +the definition of the template \tcode{expected} with +a type for the \tcode{E} parameter that +is not a valid template argument for \tcode{unexpected} is ill-formed. + +\pnum +\added{If \tcode{E} is not an lvalue reference type, }\tcode{E} shall meet the requirements of +\oldconcept{Destructible} (\tref{cpp17.destructible}). + +\rSec3[expected.void.cons]{Constructors} + +\indexlibraryctor{expected}% +\begin{itemdecl} +constexpr expected() noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\ensures +\tcode{has_value()} is \tcode{true}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +constexpr expected(const expected& rhs); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +If \tcode{rhs.has_value()} is \tcode{false}, +direct-non-list-initializes \exposid{unex} with \tcode{rhs.error()}. + +\pnum +\ensures +\tcode{rhs.has_value() == this->has_value()}. + +\pnum +\throws +Any exception thrown by the initialization of \exposid{unex}. + +\pnum +\remarks +This constructor is defined as deleted +unless \tcode{is_copy_constructible_v} is \tcode{true}. + +\pnum +This constructor is trivial +if \tcode{is_trivially_copy_constructible_v} is \tcode{true}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +constexpr expected(expected&& rhs) noexcept(is_nothrow_move_constructible_v); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_move_constructible_v} is \tcode{true}. + +\pnum +\effects +If \tcode{rhs.has_value()} is \tcode{false}, +direct-non-list-initializes \exposid{unex} with \tcode{std::move(rhs.error())}. + +\pnum +\ensures +\tcode{rhs.has_value()} is unchanged; +\tcode{rhs.has_value() == this->has_value()} is \tcode{true}. + +\pnum +\throws +Any exception thrown by the initialization of \exposid{unex}. + +\pnum +\remarks +This constructor is trivial +if \tcode{is_trivially_move_constructible_v} is \tcode{true}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + constexpr explicit(!is_convertible_v) expected(const expected& rhs); +template + constexpr explicit(!is_convertible_v) expected(expected&& rhs); +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{GF} be \tcode{const G\&} for the first overload and +\tcode{G} for the second overload. + +\pnum +\constraints +\begin{itemize} +\item +\tcode{is_void_v} is \tcode{true}; and +\item +\tcode{is_constructible_v} is \tcode{true}; and +\item +\tcode{is_constructible_v, expected\&>} +is \tcode{false}; and +\item +\tcode{is_constructible_v, expected>} +is \tcode{false}; and +\item +\tcode{is_constructible_v, const expected\&>} +is \tcode{false}; and +\item +\tcode{is_constructible_v, const expected>} +is \tcode{false}. +\end{itemize} + +\pnum +\effects +If \tcode{rhs.has_value()} is \tcode{false}, +direct-non-list-initializes \exposid{unex} +with \tcode{std::forward(rhs.er\-ror())}. + +\pnum +\ensures +\tcode{rhs.has_value()} is unchanged; +\tcode{rhs.has_value() == this->has_value()} is \tcode{true}. + +\pnum +\throws +Any exception thrown by the initialization of \exposid{unex}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + constexpr explicit(!is_convertible_v) expected(const unexpected& e); +template + constexpr explicit(!is_convertible_v) expected(unexpected&& e); +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{GF} be \tcode{const G\&} for the first overload and +\tcode{G} for the second overload. + +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\effects +Direct-non-list-initializes \exposid{unex} +with \tcode{std::forward(e.error())}. + +\pnum +\ensures +\tcode{has_value()} is \tcode{false}. + +\pnum +\throws +Any exception thrown by the initialization of \exposid{unex}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +constexpr explicit expected(in_place_t) noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\ensures +\tcode{has_value()} is \tcode{true}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + constexpr explicit expected(unexpect_t, Args&&... args); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\effects +Direct-non-list-initializes \exposid{unex} +with \tcode{std::forward(args)...}. + +\pnum +\ensures +\tcode{has_value()} is \tcode{false}. + +\pnum +\throws +Any exception thrown by the initialization of \exposid{unex}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + constexpr explicit expected(unexpect_t, initializer_list il, Args&&... args); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v\&, Args...>} is \tcode{true}. + +\pnum +\effects +Direct-non-list-initializes \exposid{unex} +with \tcode{il, std::forward(args)...}. + +\pnum +\ensures +\tcode{has_value()} is \tcode{false}. + +\pnum +\throws +Any exception thrown by the initialization of \exposid{unex}. +\end{itemdescr} + +\rSec3[expected.void.dtor]{Destructor} + +\indexlibrarydtor{expected}% +\begin{itemdecl} +constexpr ~expected(); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +If \tcode{has_value()} is \tcode{false}, destroys \exposid{unex}. + +\pnum +\remarks +If \tcode{is_trivially_destructible_v} is \tcode{true}, +then this destructor is a trivial destructor. +\end{itemdescr} + +\rSec3[expected.void.assign]{Assignment} + +\indexlibrarymember{operator=}{expected}% +\begin{itemdecl} +constexpr expected& operator=(const expected& rhs); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +\begin{itemize} +\item +If \tcode{this->has_value() \&\& rhs.has_value()} is \tcode{true}, no effects. +\item +Otherwise, if \tcode{this->has_value()} is \tcode{true}, +equivalent to: \tcode{construct_at(addressof(\exposid{unex}), rhs.\exposid{unex}); \exposid{has_val} = false;} +\item +Otherwise, if \tcode{rhs.has_value()} is \tcode{true}, +destroys \exposid{unex} and sets \exposid{has_val} to \tcode{true}. +\item +Otherwise, equivalent to \tcode{\exposid{unex} = rhs.\exposid{unex}}. +\end{itemize} + +\pnum +\returns +\tcode{*this}. + +\pnum +\remarks +This operator is defined as deleted unless +\tcode{is_copy_assignable_v} is \tcode{true} and +\tcode{is_copy_constructible_v} is \tcode{true}. + +\pnum +This operator is trivial if +\tcode{is_trivially_copy_constructible_v}, +\tcode{is_trivially_copy_assigna\-ble_v}, and +\tcode{is_trivially_destructible_v} +are all \tcode{true}. +\end{itemdescr} + +\indexlibrarymember{operator=}{expected}% +\begin{itemdecl} +constexpr expected& operator=(expected&& rhs) noexcept(@\seebelow@); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_move_constructible_v} is \tcode{true} and +\tcode{is_move_assignable_v} is \tcode{true}. + +\pnum +\effects +\begin{itemize} +\item +If \tcode{this->has_value() \&\& rhs.has_value()} is \tcode{true}, no effects. +\item +Otherwise, if \tcode{this->has_value()} is \tcode{true}, equivalent to: +\begin{codeblock} +construct_at(addressof(@\exposid{unex}@), std::move(rhs.@\exposid{unex}@)); +@\exposid{has_val}@ = false; +\end{codeblock} +\item +Otherwise, if \tcode{rhs.has_value()} is \tcode{true}, +destroys \exposid{unex} and sets \exposid{has_val} to \tcode{true}. +\item +Otherwise, equivalent to \tcode{\exposid{unex} = std::move(rhs.\exposid{unex})}. +\end{itemize} + +\pnum +\returns +\tcode{*this}. + +\pnum +\remarks +The exception specification is equivalent to +\tcode{is_nothrow_move_constructible_v \&\& is_nothrow_move_assignable_v}. + +\pnum +This operator is trivial if +\tcode{is_trivially_move_constructible_v}, +\tcode{is_trivially_move_assigna\-ble_v}, and +\tcode{is_trivially_destructible_v} +are all \tcode{true}. +\end{itemdescr} + +\indexlibrarymember{operator=}{expected}% +\begin{itemdecl} +template + constexpr expected& operator=(const unexpected& e); +template + constexpr expected& operator=(unexpected&& e); +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{GF} be \tcode{const G\&} for the first overload and +\tcode{G} for the second overload. + +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true} and +\tcode{is_assignable_v} is \tcode{true}. + +\pnum +\effects +\begin{itemize} +\item +If \tcode{has_value()} is \tcode{true}, equivalent to: +\begin{codeblock} +construct_at(addressof(@\exposid{unex}@), std::forward(e.error())); +@\exposid{has_val}@ = false; +\end{codeblock} +\item +Otherwise, equivalent to: +\tcode{\exposid{unex} = unexpected(std::forward(e.error()));} +\end{itemize} + +\pnum +\returns +\tcode{*this}. +\end{itemdescr} + +\indexlibrarymember{emplace}{expected}% +\begin{itemdecl} +constexpr void emplace() noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +If \tcode{has_value()} is \tcode{false}, +destroys \exposid{unex} and sets \exposid{has_val} to \tcode{true}. +\end{itemdescr} + +\rSec3[expected.void.swap]{Swap} + +\indexlibrarymember{swap}{expected}% +\begin{itemdecl} +constexpr void swap(expected& rhs) noexcept(@\seebelow@); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_swappable_v} is \tcode{true} and +\tcode{is_move_constructible_v} is \tcode{true}. + +\pnum +\effects +See \tref{expected.void.swap}. + +\begin{floattable}{\tcode{swap(expected\&)} effects}{expected.void.swap} +{lx{0.35\hsize}x{0.35\hsize}} +\topline +& \chdr{\tcode{this->has_value()}} & \rhdr{\tcode{!this->has_value()}} \\ \capsep +\lhdr{\tcode{rhs.has_value()}} & + no effects & + calls \tcode{rhs.swap(*this)} \\ +\lhdr{\tcode{!rhs.has_value()}} & + \seebelow & + equivalent to: \tcode{using std::swap; swap(\exposid{unex}, rhs.\exposid{unex});} \\ +\end{floattable} + +For the case where \tcode{rhs.has_value()} is \tcode{false} and +\tcode{this->has_value()} is \tcode{true}, equivalent to: +\begin{codeblock} +construct_at(addressof(@\exposid{unex}@), std::move(rhs.@\exposid{unex}@)); +destroy_at(addressof(rhs.@\exposid{unex}@)); +@\exposid{has_val}@ = false; +rhs.@\exposid{has_val}@ = true; +\end{codeblock} + +\pnum +\throws +Any exception thrown by the expressions in the \Fundescx{Effects}. + +\pnum +\remarks +The exception specification is equivalent to +\tcode{is_nothrow_move_constructible_v> \&\& is_nothrow_swappable_v>}. +\end{itemdescr} + +\indexlibrarymember{swap}{expected}% +\begin{itemdecl} +friend constexpr void swap(expected& x, expected& y) noexcept(noexcept(x.swap(y))); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Equivalent to \tcode{x.swap(y)}. +\end{itemdescr} + +\rSec3[expected.void.obs]{Observers} + +\indexlibrarymember{operator bool}{expected}% +\indexlibrarymember{has_value}{expected}% +\begin{itemdecl} +constexpr explicit operator bool() const noexcept; +constexpr bool has_value() const noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\exposid{has_val}. +\end{itemdescr} + +\indexlibrarymember{operator*}{expected}% +\begin{itemdecl} +constexpr void operator*() const noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\hardexpects +\tcode{has_value()} is \tcode{true}. +\end{itemdescr} + +\indexlibrarymember{value}{expected}% +\begin{itemdecl} +constexpr void value() const &; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{is_copy_constructible_v} is \tcode{true}. + +\pnum +\throws +\tcode{bad_expected_access(error())} if \tcode{has_value()} is \tcode{false}. +\end{itemdescr} + +\indexlibrarymember{value}{expected}% +\begin{itemdecl} +constexpr void value() &&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{is_copy_constructible_v} is \tcode{true} and +\tcode{is_move_constructible_v} is \tcode{true}. + +\pnum +\throws +\tcode{bad_expected_access(std::move(error()))} +if \tcode{has_value()} is \tcode{false}. +\end{itemdescr} + +\indexlibrarymember{error}{expected}% +\begin{itemdecl} +constexpr const E& error() const & noexcept; +constexpr E& error() & noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\hardexpects +\tcode{has_value()} is \tcode{false}. + +\pnum +\returns +\tcode{\exposid{unex}.error()}. +\end{itemdescr} + +\indexlibrarymember{error}{expected}% +\begin{itemdecl} +constexpr E&& error() && noexcept; +constexpr const E&& error() const && noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\hardexpects +\tcode{has_value()} is \tcode{false}. + +\pnum +\returns +\tcode{std::move(\exposid{unex}).error()}. +\end{itemdescr} + +\indexlibrarymember{error_or}{expected}% +\begin{itemdecl} +template constexpr E error_or(G&& e) const &; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{is_copy_constructible_v} is \tcode{true} and +\tcode{is_convertible_v} is \tcode{true}. + +\pnum +\returns +\tcode{std::forward(e)} if \tcode{has_value()} is \tcode{true}, +\tcode{error()} otherwise. +\end{itemdescr} + +\indexlibrarymember{error_or}{expected}% +\begin{itemdecl} +template constexpr E error_or(G&& e) &&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{is_move_constructible_v} is \tcode{true} and +\tcode{is_convertible_v} is \tcode{true}. + +\pnum +\returns +\tcode{std::forward(e)} if \tcode{has_value()} is \tcode{true}, +\tcode{std::move(error())} otherwise. +\end{itemdescr} + +\rSec3[expected.void.monadic]{Monadic operations} + +\indexlibrarymember{and_then}{expected}% +\begin{itemdecl} +template constexpr auto and_then(F&& f) &; +template constexpr auto and_then(F&& f) const &; +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{U} be \tcode{remove_cvref_t>}. + +\pnum +\constraints +\tcode{is_constructible_v>} is \tcode{true}. + +\pnum +\mandates +\tcode{U} is a specialization of \tcode{expected} and +\tcode{is_same_v} is \tcode{true}. + +\pnum +\effects +Equivalent to: +\begin{codeblock} +if (has_value()) + return invoke(std::forward(f)); +else + return U(unexpect, error()); +\end{codeblock} +\end{itemdescr} + +\indexlibrarymember{and_then}{expected}% +\begin{itemdecl} +template constexpr auto and_then(F&& f) &&; +template constexpr auto and_then(F&& f) const &&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{U} be \tcode{remove_cvref_t>}. + +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\mandates +\tcode{U} is a specialization of \tcode{expected} and +\tcode{is_same_v} is \tcode{true}. + +\pnum +\effects +Equivalent to: +\begin{codeblock} +if (has_value()) + return invoke(std::forward(f)); +else + return U(unexpect, std::move(error())); +\end{codeblock} +\end{itemdescr} + +\indexlibrarymember{or_else}{expected}% +\begin{itemdecl} +template constexpr auto or_else(F&& f) &; +template constexpr auto or_else(F&& f) const &; +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{G} be \tcode{remove_cvref_t>}. + +\pnum +\mandates +\tcode{G} is a specialization of \tcode{expected} and +\tcode{is_same_v} is \tcode{true}. + +\pnum +\effects +Equivalent to: +\begin{codeblock} +if (has_value()) + return G(); +else + return invoke(std::forward(f), error()); +\end{codeblock} +\end{itemdescr} + +\indexlibrarymember{or_else}{expected}% +\begin{itemdecl} +template constexpr auto or_else(F&& f) &&; +template constexpr auto or_else(F&& f) const &&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{G} be +\tcode{remove_cvref_t>}. + +\pnum +\mandates +\tcode{G} is a specialization of \tcode{expected} and +\tcode{is_same_v} is \tcode{true}. + +\pnum +\effects +Equivalent to: +\begin{codeblock} +if (has_value()) + return G(); +else + return invoke(std::forward(f), std::move(error())); +\end{codeblock} +\end{itemdescr} + +\indexlibrarymember{transform}{expected}% +\begin{itemdecl} +template constexpr auto transform(F&& f) &; +template constexpr auto transform(F&& f) const &; +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{U} be \tcode{remove_cv_t>}. + +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\mandates +\tcode{U} is a valid value type for \tcode{expected}. If \tcode{is_void_v} is +\tcode{false}, the declaration +\begin{codeblock} +U u(invoke(std::forward(f))); +\end{codeblock} +is well-formed. + +\pnum +\effects +\begin{itemize} +\item +If \tcode{has_value()} is \tcode{false}, returns +\tcode{expected(unexpect, error())}. +\item +Otherwise, if \tcode{is_void_v} is \tcode{false}, returns an +\tcode{expected} object whose \exposid{has_val} member is \tcode{true} and +\exposid{val} member is direct-non-list-initialized with +\tcode{invoke(std::forward(f))}. +\item +Otherwise, evaluates \tcode{invoke(std::forward(f))} and then returns +\tcode{expected()}. +\end{itemize} +\end{itemdescr} + +\indexlibrarymember{transform}{expected}% +\begin{itemdecl} +template constexpr auto transform(F&& f) &&; +template constexpr auto transform(F&& f) const &&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{U} be \tcode{remove_cv_t>}. + +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\mandates +\tcode{U} is a valid value type for \tcode{expected}. If \tcode{is_void_v} is +\tcode{false}, the declaration +\begin{codeblock} +U u(invoke(std::forward(f))); +\end{codeblock} +is well-formed. + +\pnum +\effects +\begin{itemize} +\item +If \tcode{has_value()} is \tcode{false}, returns +\tcode{expected(unexpect, std::move(error()))}. +\item +Otherwise, if \tcode{is_void_v} is \tcode{false}, returns an +\tcode{expected} object whose \exposid{has_val} member is \tcode{true} and +\exposid{val} member is direct-non-list-initialized with +\tcode{invoke(std::forward(f))}. +\item +Otherwise, evaluates \tcode{invoke(std::forward(f))} and then returns +\tcode{expected()}. +\end{itemize} +\end{itemdescr} + +\indexlibrarymember{transform_error}{expected}% +\begin{itemdecl} +template constexpr auto transform_error(F&& f) &; +template constexpr auto transform_error(F&& f) const &; +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{G} be \tcode{remove_cv_t>}. + +\pnum +\mandates +\tcode{G} is a valid template argument +for \tcode{unexpected}\iref{expected.un.general} and the declaration +\begin{codeblock} +G g(invoke(std::forward(f), error())); +\end{codeblock} +is well-formed. + +\pnum +\returns +If \tcode{has_value()} is \tcode{true}, \tcode{expected()}; otherwise, an +\tcode{expected} object whose \exposid{has_val} member is \tcode{false} +and \exposid{unex} member is direct-non-list-initialized with +\tcode{invoke(std::for\-ward(f), error())}. +\end{itemdescr} + +\indexlibrarymember{transform_error}{expected}% +\begin{itemdecl} +template constexpr auto transform_error(F&& f) &&; +template constexpr auto transform_error(F&& f) const &&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{G} be +\tcode{remove_cv_t>}. + +\pnum +\mandates +\tcode{G} is a valid template argument +for \tcode{unexpected}\iref{expected.un.general} and the declaration +\begin{codeblock} +G g(invoke(std::forward(f), std::move(error()))); +\end{codeblock} +is well-formed. + +\pnum +\returns +If \tcode{has_value()} is \tcode{true}, \tcode{expected()}; otherwise, an +\tcode{expected} object whose \exposid{has_val} member is \tcode{false} +and \exposid{unex} member is direct-non-list-initialized with +\tcode{invoke(std::for\-ward(f), std::move(error()))}. +\end{itemdescr} + +\rSec3[expected.void.eq]{Equality operators} + +\indexlibrarymember{operator==}{expected}% +\begin{itemdecl} +template requires is_void_v + friend constexpr bool operator==(const expected& x, const expected& y); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +The expression \tcode{x.error() == y.error()} is well-formed and +its result is convertible to \tcode{bool}. + +\pnum +\returns +If \tcode{x.has_value()} does not equal \tcode{y.has_value()}, \tcode{false}; +otherwise if \tcode{x.has_value()} is \tcode{true}, \tcode{true}; +otherwise \tcode{x.error() == y.error()}. +\end{itemdescr} + +\indexlibrarymember{operator==}{expected}% +\begin{itemdecl} +template + friend constexpr bool operator==(const expected& x, const unexpected& e); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +The expression \tcode{x.error() == e.error()} is well-formed and +its result is convertible to \tcode{bool}. + +\pnum +\returns +If \tcode{!x.has_value()} is \tcode{true}, +\tcode{x.error() == e.error()}; +otherwise \tcode{false}. +\end{itemdescr} + +\begin{addedblock} +\rSec2[expected.ref]{Partial specialization of \tcode{expected} for reference types} + +\rSec3[expected.ref.general]{General} + +\begin{codeblock} +template +class expected { +public: + using value_type = T&; + using error_type = E; + using unexpected_type = unexpected; + + template + using rebind = expected; + + // \ref{expected.ref.cons}, constructors + constexpr expected() = delete; + constexpr expected(const expected&); + constexpr expected(expected&&) noexcept(@\seebelow@); + template + constexpr explicit(@\seebelow@) expected(U&&); + template + constexpr expected(U&&) = delete; + template + constexpr explicit(@\seebelow@) expected(const expected&); + template + constexpr explicit(@\seebelow@) expected(expected&&); + template + constexpr explicit(@\seebelow@) expected(const unexpected&); + template + constexpr explicit(@\seebelow@) expected(unexpected&&); + template + constexpr expected(in_place_t, Args&&...) = delete; + template + constexpr explicit expected(unexpect_t, Args&&...); + template + constexpr explicit expected(unexpect_t, initializer_list, Args&&...); + + // \ref{expected.ref.dtor}, destructor + constexpr ~expected(); + + // \ref{expected.ref.assign}, assignment + constexpr expected& operator=(const expected&); + constexpr expected& operator=(expected&&) noexcept(@\seebelow@); + template + constexpr expected& operator=(U&&); + template + constexpr expected& operator=(const unexpected&); + template + constexpr expected& operator=(unexpected&&); + template + constexpr T& emplace(U&&) noexcept(@\seebelow@); + + // \ref{expected.ref.swap}, swap + constexpr void swap(expected&) noexcept(@\seebelow@); + friend constexpr void swap(expected& x, expected& y) noexcept(noexcept(x.swap(y))); + + // \ref{expected.ref.obs}, observers + constexpr T* operator->() const noexcept; + constexpr T& operator*() const noexcept; + constexpr explicit operator bool() const noexcept; + constexpr bool has_value() const noexcept; + constexpr T& value() const &; + constexpr T& value() &&; + constexpr const E& error() const & noexcept; + constexpr E& error() & noexcept; + constexpr const E&& error() const && noexcept; + constexpr E&& error() && noexcept; + template> constexpr remove_cv_t value_or(U&&) const; + template constexpr E error_or(G&&) const &; + + // \ref{expected.ref.monadic}, monadic operations + template constexpr auto and_then(F&& f) &; + template constexpr auto and_then(F&& f) &&; + template constexpr auto and_then(F&& f) const &; + template constexpr auto and_then(F&& f) const &&; + template constexpr auto or_else(F&& f) &; + template constexpr auto or_else(F&& f) &&; + template constexpr auto or_else(F&& f) const &; + template constexpr auto or_else(F&& f) const &&; + template constexpr auto transform(F&& f) &; + template constexpr auto transform(F&& f) &&; + template constexpr auto transform(F&& f) const &; + template constexpr auto transform(F&& f) const &&; + template constexpr auto transform_error(F&& f) &; + template constexpr auto transform_error(F&& f) &&; + template constexpr auto transform_error(F&& f) const &; + template constexpr auto transform_error(F&& f) const &&; + + // \ref{expected.ref.eq}, equality operators + template requires (!is_void_v) + friend constexpr bool operator==(const expected& x, const expected& y); + template + friend constexpr bool operator==(const expected&, const T2&); + template + friend constexpr bool operator==(const expected&, const unexpected&); + +private: + bool @\exposid{has_val}@; // \expos + union { + T* @\exposid{val}@; // \expos + unexpected @\exposid{unex}@; // \expos + }; +}; +\end{codeblock} + +\pnum +An object of type \tcode{expected} either represents a reference to an +object of type \tcode{T}, or holds an error. +Member \exposid{has_val} indicates whether the object represents a reference. +When it represents a reference, member \exposid{val} points to the referenced +object, which is not owned by the \tcode{expected} object. +Otherwise, the error is \tcode{\exposid{unex}.error()}. + +\pnum +The error is held as an \tcode{unexpected}, and not as an \tcode{E}, so that +\tcode{E} can be an lvalue reference type: an \tcode{E\&} cannot be a union +member, whereas \tcode{unexpected} holds a pointer to an external +object\iref{expected.un.ref}. + +\pnum +A program that instantiates the definition of +\tcode{expected} with a type for the \tcode{E} parameter that +is not a valid template argument for \tcode{unexpected} is ill-formed. + +\pnum +\tcode{T} shall be an object type that is not an array type. +If \tcode{E} is not an lvalue reference type, +\tcode{E} shall meet the \oldconcept{Destructible} requirements +(\tref{cpp17.destructible}). + +\rSec3[expected.ref.cons]{Constructors} + +\indexlibraryctor{expected}% +\begin{itemdecl} +constexpr expected(const expected& rhs); +constexpr expected(expected&& rhs) noexcept(@\seebelow@); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +If \tcode{rhs.has_value()} is \tcode{true}, initializes \exposid{val} with +\tcode{rhs.\exposid{val}}, so that \tcode{*this} and \tcode{rhs} refer to the +same object; otherwise, initializes \exposid{unex} with +\tcode{rhs.\exposid{unex}}. + +\pnum +\ensures +\tcode{rhs.has_value() == this->has_value()}. + +\pnum +\remarks +The exception specification of the move constructor is equivalent to +\tcode{is_nothrow_move_constructible_v>}. Each of these +constructors is trivial if the corresponding constructor of +\tcode{unexpected} is trivial, and is defined as deleted unless that +constructor of \tcode{unexpected} is available. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + constexpr explicit(@\seebelow@) expected(U&& u); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\begin{itemize} +\item \tcode{remove_cvref_t} is not \tcode{in_place_t}, +\tcode{expected}, or a specialization of \tcode{unexpected}; +\item \tcode{is_constructible_v} is \tcode{true}; and +\item \tcode{reference_constructs_from_temporary_v} is \tcode{false}. +\end{itemize} + +\pnum +\effects +Let \tcode{r} be the lvalue result of \tcode{T\& r = std::forward(u);}. +Initializes \exposid{val} with \tcode{addressof(r)}. + +\pnum +\ensures +\tcode{has_value()} is \tcode{true}. + +\pnum +\remarks +The expression inside \keyword{explicit} is equivalent to +\tcode{!is_convertible_v}. +A constructor for which +\tcode{reference_constructs_from_temporary_v} is \tcode{true} --- +one that would bind \tcode{T\&} to a temporary --- is defined as deleted. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + constexpr explicit(@\seebelow@) expected(const expected& rhs); +template + constexpr explicit(@\seebelow@) expected(expected&& rhs); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\begin{itemize} +\item \tcode{is_constructible_v} is \tcode{true}; +\item \tcode{reference_constructs_from_temporary_v} is \tcode{false}; +and +\item if \tcode{E} is an lvalue reference type, \tcode{G} is an lvalue +reference type and \tcode{is_convertible_v} is \tcode{true}; otherwise +\tcode{is_constructible_v} (for the first overload) or +\tcode{is_constructible_v} (for the second) is \tcode{true}. +\end{itemize} + +\pnum +\effects +If \tcode{rhs.has_value()} is \tcode{true}, initializes \exposid{val} with +\tcode{addressof(*rhs)}, so that \tcode{*this} refers to the object referred to +by \tcode{rhs}; otherwise, initializes \exposid{unex} with the error of +\tcode{rhs}. No object referred to by \tcode{rhs} is moved from. + +\pnum +\remarks +The expression inside \keyword{explicit} is equivalent to +\tcode{!is_convertible_v || !is_convertible_v}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + constexpr explicit(@\seebelow@) expected(const unexpected& e); +template + constexpr explicit(@\seebelow@) expected(unexpected&& e); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +If \tcode{E} is an lvalue reference type, \tcode{G} is an lvalue reference +type, \tcode{is_convertible_v} is \tcode{true}, and +\tcode{reference_constructs_from_temporary_v} is \tcode{false}; +otherwise \tcode{is_constructible_v} (for the first overload) or +\tcode{is_constructible_v} (for the second) is \tcode{true}. + +\pnum +\effects +Initializes \exposid{unex} with the error of \tcode{e}. +\tcode{has_value()} is \tcode{false}. + +\pnum +\remarks +When \tcode{E} is an lvalue reference type and \tcode{G} is not a reference +type, these constructors are defined as deleted: the referent would be the +value owned by \tcode{e}, and binding \tcode{E} to it would dangle once +\tcode{e} is destroyed. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + constexpr explicit expected(unexpect_t, Args&&... args); +template + constexpr explicit expected(unexpect_t, initializer_list il, Args&&... args); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} (respectively, +\tcode{is_constructible_v\&, Args...>}) is \tcode{true}. +If \tcode{E} is an lvalue reference type, the initialization shall not bind +\tcode{E} to a temporary. + +\pnum +\effects +Direct-non-list-initializes \exposid{unex} with \tcode{in_place} and +\tcode{std::forward(args)...} (respectively, \tcode{in_place}, +\tcode{il}, and \tcode{std::forward(args)...}), so that the error +\tcode{\exposid{unex}.error()} is constructed from those arguments. +\tcode{has_value()} is \tcode{false}. +\end{itemdescr} + +\rSec3[expected.ref.dtor]{Destructor} + +\indexlibrarydtor{expected}% +\begin{itemdecl} +constexpr ~expected(); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +If \tcode{has_value()} is \tcode{false}, destroys \exposid{unex}. + +\pnum +\remarks +This destructor is trivial if \tcode{unexpected} is trivially +destructible. \tcode{T} is not destroyed; \tcode{*this} never owns the object +it refers to. +\end{itemdescr} + +\rSec3[expected.ref.assign]{Assignment} + +\pnum +Assignment rebinds: assigning to an \tcode{expected} that holds a value +changes which object it refers to. It never assigns through to the referent. + +\indexlibrarymember{operator=}{expected}% +\begin{itemdecl} +constexpr expected& operator=(const expected& rhs); +constexpr expected& operator=(expected&& rhs) noexcept(@\seebelow@); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +If \tcode{rhs.has_value()} is \tcode{true}: if \tcode{has_value()} is +\tcode{true}, assigns \tcode{rhs.\exposid{val}} to \exposid{val}; otherwise +destroys \exposid{unex} and initializes \exposid{val} with +\tcode{rhs.\exposid{val}}. If \tcode{rhs.has_value()} is \tcode{false}, the +error of \tcode{rhs} is assigned to or used to initialize \exposid{unex}, as +for the primary template (\ref{expected.object.assign}). In every case +\tcode{*this} comes to refer to the object \tcode{rhs} refers to, or to hold the +error of \tcode{rhs}; no referenced object is assigned through. + +\pnum +\returns +\tcode{*this}. +\end{itemdescr} + +\indexlibrarymember{operator=}{expected}% +\begin{itemdecl} +template + constexpr expected& operator=(U&& u); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{remove_cvref_t} is not \tcode{expected} or a specialization of +\tcode{unexpected}, \tcode{is_constructible_v} is \tcode{true}, and +\tcode{reference_constructs_from_temporary_v} is \tcode{false}. + +\pnum +\effects +Let \tcode{r} be the lvalue result of \tcode{T\& r = std::forward(u);}. +If \tcode{has_value()} is \tcode{true}, assigns \tcode{addressof(r)} to +\exposid{val}. Otherwise, destroys \exposid{unex}, initializes \exposid{val} +with \tcode{addressof(r)}, and sets \exposid{has_val} to \tcode{true}; if +binding \tcode{r} throws, \tcode{*this} is left unchanged. + +\pnum +\returns +\tcode{*this}. +\end{itemdescr} + +\indexlibrarymember{operator=}{expected}% +\begin{itemdecl} +template + constexpr expected& operator=(const unexpected& e); +template + constexpr expected& operator=(unexpected&& e); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +If \tcode{E} is an lvalue reference type, \tcode{G} is an lvalue reference +type, \tcode{is_convertible_v} is \tcode{true}, and +\tcode{reference_constructs_from_temporary_v} is \tcode{false}; +otherwise the corresponding requirements of the primary template hold. + +\pnum +\effects +Makes \tcode{*this} hold the error of \tcode{e}, reinitializing \exposid{unex} +from \tcode{e} rather than assigning through it. When \tcode{E} is a reference +type, \tcode{\exposid{unex}.error()} thereafter refers to the same object as +\tcode{e.error()}; the previously referenced object, if any, is not modified. + +\pnum +\returns +\tcode{*this}. + +\pnum +\remarks +When \tcode{E} is an lvalue reference type and \tcode{G} is not a reference +type, these overloads are defined as deleted. +\end{itemdescr} + +\indexlibrarymember{emplace}{expected}% +\begin{itemdecl} +template + constexpr T& emplace(U&& u) noexcept(@\seebelow@); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true} and +\tcode{reference_constructs_from_temporary_v} is \tcode{false}. + +\pnum +\effects +Rebinds \tcode{*this} to refer to the object bound by +\tcode{T\& r = std::forward(u);}: if \tcode{has_value()} is \tcode{false}, +destroys \exposid{unex} first. Sets \exposid{val} to \tcode{addressof(r)} and +\exposid{has_val} to \tcode{true}. + +\pnum +\returns +\tcode{*\exposid{val}}. +\end{itemdescr} + +\rSec3[expected.ref.swap]{Swap} + +\indexlibrarymember{swap}{expected}% +\begin{itemdecl} +constexpr void swap(expected& rhs) noexcept(@\seebelow@); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Exchanges the states of \tcode{*this} and \tcode{rhs}. When both hold values, +exchanges \exposid{val} and \tcode{rhs.\exposid{val}} --- the referenced +objects are not swapped. Otherwise behaves as the primary template's +\tcode{swap} does for the error. + +\pnum +\remarks +The exception specification is equivalent to +\tcode{is_nothrow_move_constructible_v> \&\&} +\tcode{(is_reference_v || is_nothrow_swappable_v)}. +\end{itemdescr} + +\indexlibrarymember{swap}{expected}% +\begin{itemdecl} +friend constexpr void swap(expected& x, expected& y) noexcept(noexcept(x.swap(y))); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Equivalent to \tcode{x.swap(y)}. +\end{itemdescr} + +\rSec3[expected.ref.obs]{Observers} + +\indexlibrarymember{operator->}{expected}% +\indexlibrarymember{operator*}{expected}% +\begin{itemdecl} +constexpr T* operator->() const noexcept; +constexpr T& operator*() const noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\expects +\tcode{has_value()} is \tcode{true}. + +\pnum +\returns +\tcode{\exposid{val}} and \tcode{*\exposid{val}} respectively. + +\pnum +\remarks +These are \tcode{const} member functions that return a non-\tcode{const} +\tcode{T*} and \tcode{T\&}; the constness of \tcode{*this} does not propagate +to the referenced object. For deep \tcode{const}, use +\tcode{expected}. +\end{itemdescr} + +\indexlibrarymember{has_value}{expected}% +\indexlibrarymember{operator bool}{expected}% +\begin{itemdecl} +constexpr explicit operator bool() const noexcept; +constexpr bool has_value() const noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\tcode{\exposid{has_val}}. +\end{itemdescr} + +\indexlibrarymember{value}{expected}% +\begin{itemdecl} +constexpr T& value() const &; +constexpr T& value() &&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\tcode{*\exposid{val}} if \tcode{has_value()} is \tcode{true}. + +\pnum +\throws +\tcode{bad_expected_access(as_const(error()))} (respectively, +\tcode{bad_expected_access(std::move(error()))}) if \tcode{has_value()} is +\tcode{false}. Both overloads yield \tcode{T\&}; the value category of +\tcode{*this} does not affect the returned reference. +\end{itemdescr} + +\indexlibrarymember{error}{expected}% +\begin{itemdecl} +constexpr const E& error() const & noexcept; +constexpr E& error() & noexcept; +constexpr const E&& error() const && noexcept; +constexpr E&& error() && noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\expects +\tcode{has_value()} is \tcode{false}. + +\pnum +\returns +\tcode{\exposid{unex}.error()}, with the \tcode{const} and reference +qualification of the selected overload. +\end{itemdescr} + +\indexlibrarymember{value_or}{expected}% +\begin{itemdecl} +template> + constexpr remove_cv_t value_or(U&& v) const; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{is_convertible_v>} and +\tcode{is_convertible_v>} are \tcode{true}. + +\pnum +\returns +\tcode{has_value() ? static_cast>(*\exposid{val}) : +static_cast>(std::forward(v))}. The result is an object, +never a reference. +\end{itemdescr} + +\indexlibrarymember{error_or}{expected}% +\begin{itemdecl} +template + constexpr E error_or(G&& e) const &; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\tcode{has_value() ? std::forward(e) : error()}, as for the primary +template. +\end{itemdescr} + +\rSec3[expected.ref.monadic]{Monadic operations} + +\pnum +The member templates \tcode{and_then}, \tcode{or_else}, \tcode{transform}, and +\tcode{transform_error} behave as specified for the primary template +(\ref{expected.object.monadic}), with one difference: the value is passed to +the callable as \tcode{T\&} for every ref-qualification of \tcode{*this}. An +rvalue \tcode{expected} does not pass its referent as an rvalue; the +object referred to is never moved from by these operations. + +\rSec3[expected.ref.eq]{Equality operators} + +\pnum +The equality operators behave as specified for the primary template +(\ref{expected.object.eq}), comparing referents through \tcode{operator*} and +errors through \tcode{error()}. +\end{addedblock} + +%%% Local Variables: +%%% mode: LaTeX +%%% TeX-master: t +%%% End: diff --git a/papers/strip-wording.py b/papers/strip-wording.py new file mode 100755 index 0000000..6bbb386 --- /dev/null +++ b/papers/strip-wording.py @@ -0,0 +1,155 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +"""Strip working-draft change markup from a wording .tex file. + +Turns the marked-up editable copy (expected-new.tex) into clean text +suitable for a pull request against the C++ working draft: + + \\added{X} -> X + \\removed{X} -> (deleted) + \\changed{X}{Y} -> Y + \\addednb{n}{X} -> X + \\removednb{n}{X} -> (deleted) + \\changednb{n}{X}{Y} -> Y + \\remitem{X} -> (deleted) + \\begin{addedblock}..\\end{addedblock} -> unwrapped + \\begin{addedcode}..\\end{addedcode} -> unwrapped + \\begin{removedblock}..\\end{removedblock} -> (deleted) + \\begin{removedcode}..\\end{removedcode} -> (deleted) + +Brace matching is nesting-aware, so \\added{... {x} ...} is handled, as +are marks that span several lines. Any other control sequence is passed +through untouched. + +Usage: + strip-wording.py [INPUT] [-o OUTPUT] + INPUT defaults to stdin, OUTPUT to stdout. +""" + +import argparse +import re +import sys + +_CTRLWORD = re.compile(r'\\([a-zA-Z]+)') + + +def read_group(s, i): + """s[i] must be '{'. Return (inner_text, index_past_closing_brace).""" + assert s[i] == '{' + depth = 0 + start = i + while i < len(s): + c = s[i] + if c == '\\': # escaped char (\{ \} \\ \& ...): skip the pair + i += 2 + continue + if c == '{': + depth += 1 + elif c == '}': + depth -= 1 + if depth == 0: + return s[start + 1:i], i + 1 + i += 1 + raise ValueError("unbalanced braces starting at offset %d" % start) + + +def _skip_ws(s, i): + while i < len(s) and s[i] in ' \t': + i += 1 + return i + + +def _groups(s, i, n): + """Read n consecutive brace groups starting at s[i] (whitespace allowed + between them). Return (list_of_inner_texts, index_past_last) or None if + the n groups are not all present.""" + texts = [] + for _ in range(n): + i = _skip_ws(s, i) + if i >= len(s) or s[i] != '{': + return None + inner, i = read_group(s, i) + texts.append(inner) + return texts, i + + +# name -> (arity, index of the group to keep, or None to delete) +_MARKS = { + 'added': (1, 0), + 'removed': (1, None), + 'changed': (2, 1), + 'addednb': (2, 1), + 'removednb': (2, None), + 'changednb': (3, 2), + 'remitem': (1, None), +} + + +def strip_inline(s): + out = [] + i, n = 0, len(s) + while i < n: + c = s[i] + if c != '\\': + out.append(c) + i += 1 + continue + m = _CTRLWORD.match(s, i) + if not m: # backslash + non-letter, e.g. \& \{ \\ + out.append(s[i:i + 2]) + i += 2 + continue + name = m.group(1) + after = m.end() + spec = _MARKS.get(name) + if spec is not None: + arity, keep = spec + got = _groups(s, after, arity) + if got is not None: + texts, end = got + if keep is not None: + out.append(strip_inline(texts[keep])) + i = end + continue + # not a change mark (or not followed by its groups): emit verbatim + out.append(s[i:after]) + i = after + return ''.join(out) + + +def strip_environments(s): + # Remove deleted blocks entirely. + s = re.sub(r'\\begin\{removedblock\}.*?\\end\{removedblock\}[ \t]*\n?', + '', s, flags=re.DOTALL) + s = re.sub(r'\\begin\{removedcode\}.*?\\end\{removedcode\}[ \t]*\n?', + '', s, flags=re.DOTALL) + # Unwrap added blocks: drop just the begin/end markers. + for env in ('addedblock', 'addedcode'): + s = re.sub(r'[ \t]*\\begin\{%s\}[ \t]*\n?' % env, '', s) + s = re.sub(r'[ \t]*\\end\{%s\}[ \t]*\n?' % env, '', s) + return s + + +def strip(text): + return strip_inline(strip_environments(text)) + + +def main(): + ap = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawDescriptionHelpFormatter) + ap.add_argument('input', nargs='?', help='input .tex (default: stdin)') + ap.add_argument('-o', '--output', help='output file (default: stdout)') + args = ap.parse_args() + + text = (open(args.input, encoding='utf-8').read() + if args.input else sys.stdin.read()) + result = strip(text) + if args.output: + with open(args.output, 'w', encoding='utf-8') as f: + f.write(result) + else: + sys.stdout.write(result) + + +if __name__ == '__main__': + main() diff --git a/papers/wg21-latex/implementation.hpp b/papers/wg21-latex/implementation.hpp index a0dcc7c..348b3c9 100644 --- a/papers/wg21-latex/implementation.hpp +++ b/papers/wg21-latex/implementation.hpp @@ -1,4 +1,4 @@ -// papers/wg21-latex/implementation.hpp -*-C++-*- +// papers/wg21-latex/implementation.hpp -*-C++-*- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // ---------------------- // BASE AND DETAILS ELIDED