From 8bc343b78578fad14e6f55533db97122ce5f8e32 Mon Sep 17 00:00:00 2001 From: Steve Downey Date: Wed, 23 Sep 2026 07:27:09 -0400 Subject: [PATCH 1/4] Makefile improvement for paper --- papers/D4280R0.tex | 16 ---------------- papers/Makefile | 31 +++++++++++++++++++++++++------ 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/papers/D4280R0.tex b/papers/D4280R0.tex index e760ad5..f8db799 100644 --- a/papers/D4280R0.tex +++ b/papers/D4280R0.tex @@ -774,20 +774,4 @@ \chapter*{Document history} \bibliographystyle{abstract} \bibliography{wg21,mybiblio} -\backmatter -\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} - \end{document} diff --git a/papers/Makefile b/papers/Makefile index 214c70f..1e4c5d0 100644 --- a/papers/Makefile +++ b/papers/Makefile @@ -60,12 +60,31 @@ D4280R0.pdf : D4280R0.tex expected-new.tex wg21.bib 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 $@ +strip-wording: expected-clean.tex ## Assemble clean [expected] wording for a working-draft pull request + +# expected-clean.tex is the clean [expected] wording to submit as a pull request +# against the C++ working draft. Its subclause bodies are the specgen-generated +# fragments -- the source of truth -- assembled by the repository-root makefile +# into wording/expected.tex, so this file cannot lag the annotated headers. Only +# [expected.general] and [expected.syn] are hand-authored prose specgen does not +# emit; they are taken from expected-new.tex, everything above the first +# generated clause, with its \added/addedblock change markup stripped. +expected-clean.tex: expected-new.tex strip-wording.py wording/expected.tex + { sed '/\\rSec2\[expected\.unexpected\]/,$$d' expected-new.tex \ + | python3 strip-wording.py; \ + cat wording/expected.tex; } > $@.tmp + mv $@.tmp $@ + +# wording/expected.tex is generated from the annotated headers by the +# repository-root makefile. Delegate to it unconditionally, so a build started +# here rather than by `make papers` from the root still regenerates any clause a +# header edit staled; the root rule rewrites the file only when something +# changed, so this stays a no-op -- and expected-clean.tex only rebuilds -- when +# nothing did. +.PHONY: FORCE +wording/expected.tex: FORCE + $(MAKE) -C .. wording +FORCE: define curl_cmd = curl https://wg21.link/index.bib > wg21.bib From 4becb6dfc17f0f80f5547857d9250db3670330e2 Mon Sep 17 00:00:00 2001 From: Steve Downey Date: Mon, 28 Sep 2026 22:34:23 -0400 Subject: [PATCH 2/4] paper: tighten D4280 prose and keep it to C++29 Remove the cost/buy metaphors and other stock phrasing, and trim "not X, but Y" tails and repeated "idiom"/"precisely". Drop everything that only served the reference implementation or a pre-C++29 language floor: D10 no longer describes staying at C++20 behind a deleted-function-message fallback, the Beman-only detection macro section (old D11) is gone, and the implementation notes at the end of D12 are removed. Remaining decisions are renumbered D11 and D12. Fix the dangling \nameref{chap:impl}, and add the [version.syn] edit bumping __cpp_lib_expected and __cpp_lib_freestanding_expected, following P2988's treatment of optional. --- papers/D4280R0.tex | 111 +++++++++++++++++---------------------------- 1 file changed, 42 insertions(+), 69 deletions(-) diff --git a/papers/D4280R0.tex b/papers/D4280R0.tex index f8db799..8b51c51 100644 --- a/papers/D4280R0.tex +++ b/papers/D4280R0.tex @@ -152,8 +152,7 @@ \section{\tcode{expected, E>}} \section{Monadic composition is uniform} 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. +a chain of fallible steps never has to leave \tcode{expected}. \begin{tabular}{ lr } \begin{minipage}[t]{0.45\columnwidth} @@ -206,7 +205,7 @@ \section{An error you do not own} \end{minipage} \end{tabular} -None of the ``after'' column is new machinery. Each is the existing +Nothing in the ``after'' column is new API. Each entry is the existing \tcode{expected} API with a reference where an object used to be. \chapter{The proposal} @@ -231,7 +230,7 @@ \chapter{The proposal} 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 +permitting a reference \tcode{E} means relaxing a constraint. 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 @@ -364,17 +363,17 @@ \chapter{The proposal} \end{codeblock} The Beman reference implementation is available online -\cite{Downey_beman_expected}. It is described in \nameref{chap:impl}. +\cite{Downey_beman_expected}. \chapter{Motivation} 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 +performance-sensitive code, because they do not copy. When such a function can fail, there is no good return type today: \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 +\item \tcode{expected} needs 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 @@ -404,12 +403,12 @@ \chapter{Motivation} 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{optional} rebinds. The observable behavior differs. However, the +same difference 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. +fallible sum type is not a special case that warrants an exception. \tcode{optional} is \tcode{variant}; \tcode{expected} is \tcode{variant}. \tcode{tuple} and \tcode{pair} can hold references; @@ -458,7 +457,7 @@ \section{Rebind on assignment, never assign-through (D1)} the same question for \tcode{optional}, and the survey behind it \cite{P1683R0} records assign-through implementations that were abandoned. \tcode{emplace} rebinds too --- it is the only thing it can do --- and -\tcode{swap} exchanges the two pointers, not the two referents. +\tcode{swap} exchanges the two pointers and leaves the referents alone. \section{Shallow \tcode{const} (D2)} @@ -470,8 +469,8 @@ \section{Shallow \tcode{const} (D2)} 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. +promise --- a \tcode{T* const} does not either --- and the mistake is in +reading \tcode{const} on a handle as \tcode{const} on the referent. \section{Dangling constructors are deleted, with a diagnostic (D3)} @@ -481,8 +480,7 @@ \section{Dangling constructors are deleted, with a diagnostic (D3)} 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 +One wrinkle 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 @@ -502,8 +500,7 @@ \section{References in the error position, by relaxation not specialization (D4) 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 +admitting a reference \tcode{E} only requires relaxing a constraint. 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 @@ -554,7 +551,7 @@ \section{\tcode{value\_or} returns by value; \tcode{error\_or} mirrors it (D6)} \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 +\tcode{= delete}d with a message, not left absent, so the error names the reason instead of leaving the reader to infer it from ``no member named \tcode{value_or}.'' @@ -583,8 +580,7 @@ \section{Shallow conversions must not steal (D9)} 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: +pitfall, stated as a guarantee callers may rely on: \begin{minted}[fontsize=\small]{c++} std::string s = "bar"; @@ -598,33 +594,21 @@ \section{Shallow conversions must not steal (D9)} 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. -\section{Deleted functions carry messages, where the compiler allows it (D10)} +\section{Deleted functions carry messages (D10)} Every deleted operation says what is wrong and what to do instead. For example, the deleted default constructor explains that \tcode{T\&} cannot be null. -Deleted-function messages \cite{P2573R2} are \CppXXVI{}; this proposal does not -raise the library's floor to get them. A macro emits \tcode{= delete("...")} -when \tcode{__cpp_deleted_function} advertises support, and falls back to plain -\tcode{= delete} otherwise. The reference implementation, and this proposal, -stay at \CppXX{}. A good diagnostic is useful when free, but not worth a higher -language-version floor. - -\section{Reference-implementation detection macro (D11)} - -The reference implementation defines the Beman-owned macro -\tcode{BEMAN_EXPECTED_HAS_REFERENCES} as \tcode{1} in its shared public -configuration. It is available to textual-header consumers; importing the -\tcode{beman.expected} module does not import macros, and module support is -unconditional. No reserved standard-library feature-test macro is claimed -before standardization assigns one. +Deleted-function messages \cite{P2573R2} are \CppXXVI{}, so every +implementation of this proposal has them. The wording does not specify the +message text. \section{The held \tcode{unexpected} is exposition-only; the observable - behavior is keyed on \tcode{E} (D12)} + behavior is keyed on \tcode{E} (D11)} Storing the error as \tcode{unexpected} (D4) is a specification device, not a -representation mandate. It buys the reference case --- \tcode{unexpected} -holds a pointer --- without a new specialization. It must buy nothing else. In -particular it must not reach an existing implementation and tell it to change +representation mandate. It supplies the reference case --- +\tcode{unexpected} holds a pointer --- without a new specialization, and +that is its only purpose. In particular it must not reach an existing implementation and tell it to change what \tcode{expected} stores. So the exposition-only member is \tcode{unexpected}, but every observable @@ -633,8 +617,8 @@ \section{The held \tcode{unexpected} is exposition-only; the observable member function read exactly as they do in the current \tcode{T}-and-\tcode{E} wording. An implementation that stores \tcode{E} directly, as every shipping one does, is conforming unchanged. Its layout, its name mangling, and its trait -answers do not move. ABI stability for existing implementations was a goal of -this design, not an accident of it. +answers do not move. The design targets ABI stability for existing +implementations. This is safe because \tcode{unexpected} is a transparent wrapper over \tcode{E}: for an object \tcode{E} its copy, move, assignment, destruction, @@ -645,36 +629,15 @@ \section{The held \tcode{unexpected} is exposition-only; the observable its copy, and lands on the same answer. The equivalence was checked trait by trait against \tcode{std::expected} across a battery of awkward error types. -The design commits to the conclusion, not to the coincidence. If an observable +If an observable difference between the \tcode{unexpected} rendering and a rendering with separate \tcode{T} and \tcode{E} members is ever found, that is a defect in this proposal, and its resolution is already decided: the behavior is whatever the \tcode{T}-and-\tcode{E} rendering gives. The wrapper is how the error is described, never what the program is allowed to observe. -The reference implementation also preserves the existing conservative -exception specifications for assignment when \tcode{E} is a reference. Those -specifications remain expressed in terms of \tcode{E}, and can therefore report -that assignment is potentially throwing even though the implementation merely -rebinds a pointer. This avoids broadening the proposal with a second observable -semantic change; tests lock in that conservative result. - -One implementation note, since it is not obvious and cost real debugging time. -Declaring a copy or move constructor as two candidates, each guarded by its own -\tcode{requires}-clause covering only the trivial and only the non-trivial case, -leaves a gap when \tcode{E} is not copy or move constructible at all: neither -candidate's constraint holds, and whether the compiler still falls back to an -implicitly-declared (and correctly deleted) constructor is exactly the kind of -question compilers have disagreed about. \tcode{libstdc++}'s own -\tcode{} avoids the question by declaring the trivial-path constructor -with no constraint at all --- an unconditional \tcode{= default} --- and letting -constraint subsumption prefer the non-trivial candidate whenever it applies. The -reference implementation adopts the same idiom. It is a statement about how to -spell the declaration, not about what either candidate computes, and it changes -nothing this section already promised. - \section{\tcode{expected} is trivially copyable when \tcode{T} and - \tcode{E} are (D13)} + \tcode{E} are (D12)} A drive-by. \tcode{expected} is not trivially copyable today, because \tcode{expected}'s copy and move assignment are specified as non-trivial @@ -720,7 +683,7 @@ \section{``Wait for \tcode{variant} and do it all at once''} \tcode{tuple} and \tcode{pair} can hold references today; \tcode{variant} cannot. Extending \tcode{variant} is a separate, larger job. -\tcode{expected} can be specified precisely right now by following +\tcode{expected} can be specified right now by following \tcode{optional} line for line. A clean, ready feature should not wait on an unrelated one. @@ -734,17 +697,18 @@ \chapter{Impact on the standard} 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. +feature. \chapter{Wording} 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} +current working draft, reproduced below, and of two feature-test macros in +\tcode{[version.syn]}: 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. -The edits are of two kinds. A new \tcode{[expected.ref]} subclause specifies +The \tcode{[expected]} 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 @@ -754,6 +718,15 @@ \chapter{Wording} construction rule of D5. The marked edits included here are the proposed changes. +In \tcode{[version.syn]}, header \tcode{} synopsis, update the values +of the following macros, with the value selected by the editor to reflect the +date of adoption of this paper: + +\begin{codeblock} +#define @\defnlibxname{cpp_lib_expected}@ @\removed{202606L}\added{20XXXXL}@ // also in \libheader{expected} +#define @\defnlibxname{cpp_lib_freestanding_expected}@ @\removed{202311L}\added{20XXXXL}@ // freestanding, also in \libheader{expected} +\end{codeblock} + \include{expected-new} \chapter{Acknowledgments} From 1e1ed46c1ea2839c094e631bd474da575e1123ce Mon Sep 17 00:00:00 2001 From: Steve Downey Date: Mon, 12 Oct 2026 11:28:14 -0400 Subject: [PATCH 3/4] Build the paper without specgen, and check the wording against the headers `make papers` already regenerated the [expected] wording through specgen's depfile before building the paper. Without specgen on PATH it now warns and uses the checked-in fragments as they are, so the paper still builds anywhere; the fragments are not touched, so the warning repeats until specgen regenerates them. `make wording-check` regenerates into a scratch directory and compares the assembled expected.tex and each checked-in fragment, failing if the checked-in wording has drifted from the headers. The specgen invocation and the assembly are now shared definitions, so the check and the real rule cannot drift apart. The papers target also assembles expected-clean.tex, the clean wording for a working-draft pull request. --- Makefile | 76 +++++++++++++++++++++++++++++++++++-------------- papers/Makefile | 2 +- 2 files changed, 56 insertions(+), 22 deletions(-) diff --git a/Makefile b/Makefile index 4f1ff44..37294ba 100755 --- a/Makefile +++ b/Makefile @@ -154,7 +154,7 @@ env: $(foreach v, $(.VARIABLES), $(info $(v) = $($(v)))) .PHONY: papers -papers: wording +papers: wording ## Regenerate the wording if needed, then rebuild the paper(s) $(MAKE) -C papers papers # ---- Generated [expected] wording ------------------------------------------ @@ -199,34 +199,68 @@ ifneq ($(SPECGEN_GCC_TOOLCHAIN),) SPECGEN_CLANG_ARGS += --gcc-toolchain=$(SPECGEN_GCC_TOOLCHAIN) endif +# One specgen run over the three headers, splitting into $(1). Any further +# options, $(2), follow the fixed ones; the grouped rule below passes the +# depfile through them, and wording-check passes none. +define specgen_wording +$(SPECGEN) generate $(WORDING_HEADERS) \ + --backend latex --validate --base-section-depth 2 \ + --split $(1) \ + --root expected.unexpected.root \ + --root expected.bad.root \ + --root expected.root \ + $(2) \ + --no-compile-commands -- $(SPECGEN_CLANG_ARGS) +endef + +# The fragments of $(1)/fragments assembled, in clause order, into +# $(1)/expected.tex. Written to a temporary first: a half-written +# expected.tex that make believes is finished is worse than no expected.tex +# at all. +define assemble_wording +cat $(WORDING_DIR)/preamble.tex > $(1)/expected.tex.tmp +for clause in $(WORDING_CLAUSES); do \ + printf '\n' >> $(1)/expected.tex.tmp; \ + cat $(1)/fragments/$$clause.tex >> $(1)/expected.tex.tmp; \ +done +mv $(1)/expected.tex.tmp $(1)/expected.tex +endef + # A grouped target (GNU Make 4.3+): one invocation writes all of these, and -# make must not run it once per fragment. +# make must not run it once per fragment. Without specgen the checked-in +# fragments are used as they are, with a warning, so the paper still builds; +# they are not touched, so the warning repeats until specgen regenerates them. $(WORDING_FRAGMENTS) &: $(WORDING_HEADERS) - @mkdir -p papers/.deps - $(SPECGEN) generate $(WORDING_HEADERS) \ - --backend latex --validate --base-section-depth 2 \ - --split $(WORDING_DIR)/fragments \ - --root expected.unexpected.root \ - --root expected.bad.root \ - --root expected.root \ - --depfile papers/.deps/wording.d \ - $(addprefix --dep-target ,$(WORDING_FRAGMENTS)) \ - --no-compile-commands -- $(SPECGEN_CLANG_ARGS) - -# Assembled into a temporary first: a half-written expected.tex that make -# believes is finished is worse than no expected.tex at all. + @if command -v $(SPECGEN) > /dev/null 2>&1; then \ + mkdir -p papers/.deps && \ + $(call specgen_wording,$(WORDING_DIR)/fragments, \ + --depfile papers/.deps/wording.d \ + $(addprefix --dep-target ,$(WORDING_FRAGMENTS))); \ + else \ + echo "warning: '$(SPECGEN)' not found; using the checked-in wording" >&2; \ + fi + $(WORDING_DIR)/expected.tex: $(WORDING_DIR)/preamble.tex $(WORDING_FRAGMENTS) - @cat $(WORDING_DIR)/preamble.tex > $@.tmp - @for clause in $(WORDING_CLAUSES); do \ - printf '\n' >> $@.tmp; \ - cat $(WORDING_DIR)/fragments/$$clause.tex >> $@.tmp; \ - done - @mv $@.tmp $@ + @$(call assemble_wording,$(WORDING_DIR)) @echo "Wrote $@" .PHONY: wording wording: $(WORDING_DIR)/expected.tex ## Regenerate papers/wording/ from the annotated headers via specgen +# Regenerates into a scratch directory and compares, so the checked-in +# wording is never touched. Only what is checked in is compared: specgen also +# writes the *.root.tex fragments, which are not. +.PHONY: wording-check +wording-check: ## Check the checked-in wording matches what the headers generate + rm -rf $(BUILD_DIR)/wording-check + mkdir -p $(BUILD_DIR)/wording-check/fragments + $(call specgen_wording,$(BUILD_DIR)/wording-check/fragments) > /dev/null + @$(call assemble_wording,$(BUILD_DIR)/wording-check) + diff $(WORDING_DIR)/expected.tex $(BUILD_DIR)/wording-check/expected.tex + for fragment in $(notdir $(WORDING_FRAGMENTS)); do \ + diff $(WORDING_DIR)/fragments/$$fragment $(BUILD_DIR)/wording-check/fragments/$$fragment || exit 1; \ + done + # What specgen read to produce the fragments -- including headers reached only # through an #include, which is the edge a hand-written prerequisite list # forgets. Written by --depfile above; absent until the first run, hence diff --git a/papers/Makefile b/papers/Makefile index 1e4c5d0..204069c 100644 --- a/papers/Makefile +++ b/papers/Makefile @@ -52,7 +52,7 @@ $(DEPS_DIR): mkdir -p $(DEPS_DIR) .PHONY: papers -papers: D4280R0.pdf ## Compile papers +papers: D4280R0.pdf expected-clean.tex ## Compile papers and assemble the clean wording D4280R0.pdf : D4280R0.tex expected-new.tex wg21.bib From 8775e6f5f162291565961de4a158f2409fa6ccde Mon Sep 17 00:00:00 2001 From: Steve Downey Date: Mon, 19 Oct 2026 00:03:01 -0400 Subject: [PATCH 4/4] Regenerate the [expected] wording with specgen 0.4.0 specgen now wraps each synopsis in `namespace std { ... }`, as the working draft does, which reindents every synopsis line. It also honors the `\merge` on the expected destructor pair: the synopsis shows the single `constexpr ~expected();` and the itemdescr drops the Constraints paragraph, leaving the Effects and the triviality Remarks, matching the draft. Ignoring whitespace, those are the only changes. `make wording-check` passes against the regenerated files. --- papers/wording/expected.tex | 2403 +++++++++-------- papers/wording/fragments/expected.bad.tex | 28 +- .../wording/fragments/expected.bad.void.tex | 34 +- .../wording/fragments/expected.expected.tex | 852 +++--- papers/wording/fragments/expected.ref.tex | 714 ++--- .../wording/fragments/expected.unexpected.tex | 130 +- papers/wording/fragments/expected.void.tex | 643 +++-- 7 files changed, 2411 insertions(+), 2393 deletions(-) diff --git a/papers/wording/expected.tex b/papers/wording/expected.tex index 5194ce0..2d42b68 100644 --- a/papers/wording/expected.tex +++ b/papers/wording/expected.tex @@ -22,50 +22,52 @@ \rSec3[expected.un.general]{General} \begin{codeblock} -template -class @\libglobal{unexpected}@ { - // [expected.un.general] para 2: ill-formed instantiations - -public: - constexpr unexpected(const unexpected&) = default; - constexpr unexpected(unexpected&&) = default; - - template - requires(!is_same_v, unexpected> && - !is_same_v, in_place_t> && is_constructible_v) - constexpr explicit unexpected(Err&& e) noexcept(is_nothrow_constructible_v); - - template - requires is_constructible_v - constexpr explicit unexpected(in_place_t, Args&&... args) noexcept( - is_nothrow_constructible_v); - - template - requires is_constructible_v&, Args...> - constexpr explicit unexpected( - in_place_t, initializer_list il, - Args&&... args) noexcept(is_nothrow_constructible_v&, - 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(is_nothrow_swappable_v); - - template - friend constexpr bool operator==(const unexpected& x, const unexpected& y); - - friend constexpr void swap(unexpected& x, unexpected& y) noexcept(noexcept(x.swap(y))) - requires is_swappable_v; - -private: - E @\exposidnc{unex}@; // exposition only -}; +namespace std { + template + class @\libglobal{unexpected}@ { + // [expected.un.general] para 2: ill-formed instantiations + + public: + constexpr unexpected(const unexpected&) = default; + constexpr unexpected(unexpected&&) = default; + + template + requires(!is_same_v, unexpected> && + !is_same_v, in_place_t> && is_constructible_v) + constexpr explicit unexpected(Err&& e) noexcept(is_nothrow_constructible_v); + + template + requires is_constructible_v + constexpr explicit unexpected(in_place_t, Args&&... args) noexcept( + is_nothrow_constructible_v); + + template + requires is_constructible_v&, Args...> + constexpr explicit unexpected( + in_place_t, initializer_list il, + Args&&... args) noexcept(is_nothrow_constructible_v&, + 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(is_nothrow_swappable_v); + + template + friend constexpr bool operator==(const unexpected& x, const unexpected& y); + + friend constexpr void swap(unexpected& x, unexpected& y) noexcept(noexcept(x.swap(y))) + requires is_swappable_v; + + private: + E @\exposidnc{unex}@; // exposition only + }; +} \end{codeblock} \begin{itemdescr} @@ -235,31 +237,33 @@ \rSec3[expected.un.ref]{Partial specialization 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; +namespace std { + 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 unexpected& operator=(const unexpected&) = default; + constexpr unexpected& operator=(unexpected&&) = default; - constexpr E& error() const noexcept; + constexpr E& error() const noexcept; - constexpr void swap(unexpected& other) noexcept; + constexpr void swap(unexpected& other) noexcept; - template - friend constexpr bool operator==(const unexpected&, const unexpected&); + template + friend constexpr bool operator==(const unexpected&, const unexpected&); - friend constexpr void swap(unexpected& x, unexpected& y) noexcept; + friend constexpr void swap(unexpected& x, unexpected& y) noexcept; -private: - E* unex; // exposition only -}; + private: + E* unex; // exposition only + }; +} \end{codeblock} \begin{itemdescr} @@ -378,19 +382,21 @@ \end{itemdescr} \begin{codeblock} -template -class @\libglobal{bad_expected_access}@ : public bad_expected_access { -public: - BEMAN_EXPECTED_CONSTEXPR_EXCEPTION explicit bad_expected_access(E e); - BEMAN_EXPECTED_CONSTEXPR_EXCEPTION 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}@; // exposition only -}; +namespace std { + template + class @\libglobal{bad_expected_access}@ : public bad_expected_access { + public: + BEMAN_EXPECTED_CONSTEXPR_EXCEPTION explicit bad_expected_access(E e); + BEMAN_EXPECTED_CONSTEXPR_EXCEPTION 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}@; // exposition only + }; +} \end{codeblock} \begin{itemdescr} @@ -468,23 +474,25 @@ \rSec2[expected.bad.void]{Class template specialization bad_expected_access} \begin{codeblock} -template<> -class @\libglobal{bad_expected_access}@ : public exception { -protected: - BEMAN_EXPECTED_CONSTEXPR_EXCEPTION bad_expected_access() noexcept = default; - BEMAN_EXPECTED_CONSTEXPR_EXCEPTION bad_expected_access( - const bad_expected_access&) noexcept = default; - BEMAN_EXPECTED_CONSTEXPR_EXCEPTION bad_expected_access( - bad_expected_access&&) noexcept = default; - BEMAN_EXPECTED_CONSTEXPR_EXCEPTION bad_expected_access& operator=( - const bad_expected_access&) noexcept = default; - BEMAN_EXPECTED_CONSTEXPR_EXCEPTION bad_expected_access& operator=( - bad_expected_access&&) noexcept = default; - BEMAN_EXPECTED_CONSTEXPR_EXCEPTION ~bad_expected_access() = default; - -public: - BEMAN_EXPECTED_CONSTEXPR_EXCEPTION const char* what() const noexcept override; -}; +namespace std { + template<> + class @\libglobal{bad_expected_access}@ : public exception { + protected: + BEMAN_EXPECTED_CONSTEXPR_EXCEPTION bad_expected_access() noexcept = default; + BEMAN_EXPECTED_CONSTEXPR_EXCEPTION bad_expected_access( + const bad_expected_access&) noexcept = default; + BEMAN_EXPECTED_CONSTEXPR_EXCEPTION bad_expected_access( + bad_expected_access&&) noexcept = default; + BEMAN_EXPECTED_CONSTEXPR_EXCEPTION bad_expected_access& operator=( + const bad_expected_access&) noexcept = default; + BEMAN_EXPECTED_CONSTEXPR_EXCEPTION bad_expected_access& operator=( + bad_expected_access&&) noexcept = default; + BEMAN_EXPECTED_CONSTEXPR_EXCEPTION ~bad_expected_access() = default; + + public: + BEMAN_EXPECTED_CONSTEXPR_EXCEPTION const char* what() const noexcept override; + }; +} \end{codeblock} \rSec2[expected.expected]{Class template expected} @@ -492,436 +500,438 @@ \rSec3[expected.object.general]{General} \begin{codeblock} -template -class @\libglobal{expected}@ { -private: - using @\exposidnc{error-value-type}@ = remove_cv_t>; // exposition only - -public: - using @\libmember{value_type}{expected}@ = T; - using @\libmember{error_type}{expected}@ = E; - using @\libmember{unexpected_type}{expected}@ = unexpected; - - template using rebind = expected; - - // ------------------------------------------------------------------------- - // [expected.object.cons] Constructors - // ------------------------------------------------------------------------- - - // Default constructor: value-initializes T - constexpr expected() noexcept(is_nothrow_default_constructible_v) - requires is_default_constructible_v; - - // Copy constructor (trivial path). Unconstrained on purpose: this is the - // sole declaration when T or E is not copy constructible at all (it is - // then implicitly defined as deleted), and the more-constrained - // non-trivial-path overload below is selected over it by constraint - // subsumption whenever it is viable. - constexpr expected(const expected&) = default; - - // Copy constructor (non-trivial path) - constexpr expected(const expected& rhs) noexcept(is_nothrow_copy_constructible_v && - is_nothrow_copy_constructible_v) - requires(is_copy_constructible_v && is_copy_constructible_v && - !(is_trivially_copy_constructible_v && - is_trivially_copy_constructible_v)); - - // Move constructor (trivial path). Unconstrained; see the copy - // constructor above for why. No explicit noexcept: let the compiler - // deduce it, so a non-movable-at-all E deletes rather than mismatches. - constexpr expected(expected&&) = default; - - // Move constructor (non-trivial path) - constexpr expected(expected&& rhs) noexcept(is_nothrow_move_constructible_v && - is_nothrow_move_constructible_v) - requires(is_move_constructible_v && is_move_constructible_v && - !(is_trivially_move_constructible_v && - is_trivially_move_constructible_v)); - - // Converting copy constructor from expected — value-E path - template - requires(!is_reference_v && is_constructible_v && - is_constructible_v && - (is_same_v> || - !@\exposidnc{converts-from-any-cvref}@>) && - !is_constructible_v, expected&> && - !is_constructible_v, expected&&> && - !is_constructible_v, const expected&> && - !is_constructible_v, const expected&&>) - constexpr explicit(!is_convertible_v || !is_convertible_v) - expected(const expected& rhs); - - // Converting move constructor from expected — value-E path - template - requires(!is_reference_v && is_constructible_v && - is_constructible_v && - (is_same_v> || - !@\exposidnc{converts-from-any-cvref}@>) && - !is_constructible_v, expected&> && - !is_constructible_v, expected&&> && - !is_constructible_v, const expected&> && - !is_constructible_v, const expected&&>) - constexpr explicit(!is_convertible_v || !is_convertible_v) - expected(expected&& rhs); - - // Converting constructor from expected — reference-E path: only accepts sources - // whose error type G is itself a reference convertible to E. - template - requires(is_reference_v && is_reference_v && - is_constructible_v && is_convertible_v) - constexpr explicit(!is_convertible_v || !is_convertible_v) - expected(const expected& rhs); - - template - requires(is_reference_v && is_reference_v && is_constructible_v && - is_convertible_v) - constexpr explicit(!is_convertible_v || !is_convertible_v) - expected(expected&& rhs); - - // Constructor from value U&& - template> - requires(!is_same_v, in_place_t> && - !is_same_v, unexpect_t> && - !is_same_v, expected> && is_constructible_v && - !@\exposidnc{is-unexpected-specialization}@>::value && - (!is_same_v> || - !@\exposidnc{is-expected-specialization}@>::value)) - constexpr explicit(!is_convertible_v) expected(U&& v); - - // Constructor from unexpected const& / && — value-E path - template - requires(!is_reference_v && is_constructible_v) - constexpr explicit(!is_convertible_v) expected(const unexpected& e); - - template - requires(!is_reference_v && is_constructible_v) - constexpr explicit(!is_convertible_v) expected(unexpected&& e); - - // Constructor from unexpected — reference-E path. Allowed only when G is itself a - // reference, i.e. the source unexpected holds a reference to an external object, so - // binding E& to e.error() cannot dangle regardless of the source's value category. No - // const_cast is needed. - template - requires(is_reference_v && is_reference_v && is_constructible_v && - !reference_constructs_from_temporary_v) - constexpr explicit(!is_convertible_v) expected(const unexpected& e) noexcept; - - template - requires(is_reference_v && is_reference_v && is_constructible_v && - !reference_constructs_from_temporary_v) - constexpr explicit(!is_convertible_v) expected(unexpected&& e) noexcept; - - // Deleted for reference E with value G: the referent lives inside the unexpected - // object, so binding E& to it would dangle once a temporary source is destroyed. Use - // (unexpect, lvalue), or an unexpected holding an external object, instead. - template - requires(is_reference_v && !is_reference_v) - constexpr expected(const unexpected&) = BEMAN_EXPECTED_DELETE_MSG( - "expected: cannot construct from unexpected; the value would dangle " - "— use unexpected"); - - template - requires(is_reference_v && !is_reference_v) - constexpr expected(unexpected&&) = BEMAN_EXPECTED_DELETE_MSG( - "expected: cannot construct from unexpected; the value would dangle " - "— use unexpected"); - - // In-place constructor for value - template - requires is_constructible_v - constexpr explicit expected(in_place_t, Args&&... args); - - // In-place constructor for value with initializer_list - template - requires is_constructible_v&, Args...> - constexpr explicit expected(in_place_t, initializer_list il, Args&&... args); - - // In-place constructor for error - template - requires(is_constructible_v && !@\exposidnc{unexpect-dangles-v}@) - constexpr explicit expected(unexpect_t, Args&&... args); - - // Deleted: single argument would bind E& to a temporary — dangling prevention - template - requires(@\exposidnc{unexpect-dangles-v}@) - constexpr expected(unexpect_t, Args&&...) = - BEMAN_EXPECTED_DELETE_MSG("expected: unexpect argument would bind a " - "temporary that dangles; pass an lvalue reference"); - - // Deleted catch-all: reference E, argument neither constructible nor a dangling case - // (e.g. binding a non-const E& from a const lvalue). - template - requires(is_reference_v && !is_constructible_v && - !@\exposidnc{unexpect-dangles-v}@) - constexpr expected(unexpect_t, Args&&...) = BEMAN_EXPECTED_DELETE_MSG( - "expected: no viable conversion from the given argument(s) to E&"); - - // In-place constructor for error with initializer_list - template - requires(!is_reference_v && is_constructible_v&, Args...>) - constexpr explicit expected(unexpect_t, initializer_list il, Args&&... args); - - template - requires is_reference_v - constexpr expected(unexpect_t, initializer_list, Args&&...) = - BEMAN_EXPECTED_DELETE_MSG("expected: initializer-list error construction " - "cannot bind a reference; pass an lvalue reference"); - - // ------------------------------------------------------------------------- - // [expected.object.dtor] Destructor - // ------------------------------------------------------------------------- - - constexpr ~expected() - requires(is_trivially_destructible_v && is_trivially_destructible_v) - = default; - - constexpr ~expected() - requires(!(is_trivially_destructible_v && is_trivially_destructible_v)); - - // ------------------------------------------------------------------------- - // [expected.object.assign] Assignment - // ------------------------------------------------------------------------- - - // Copy assignment (trivial path) - constexpr expected& operator=(const expected&) - requires(is_trivially_copy_constructible_v && - is_trivially_copy_assignable_v && is_trivially_destructible_v && - is_trivially_copy_constructible_v && - is_trivially_copy_assignable_v && is_trivially_destructible_v) - = default; - - // Copy assignment (non-trivial path) - constexpr expected& operator=(const expected& rhs) noexcept( - is_nothrow_copy_constructible_v && is_nothrow_copy_assignable_v && - is_nothrow_copy_constructible_v && is_nothrow_copy_assignable_v) - requires(is_copy_constructible_v && is_copy_assignable_v && - (is_reference_v || - (is_copy_constructible_v && is_copy_assignable_v)) && - (is_nothrow_move_constructible_v || - is_nothrow_move_constructible_v) && - !(is_trivially_copy_constructible_v && +namespace std { + template + class @\libglobal{expected}@ { + private: + using @\exposidnc{error-value-type}@ = remove_cv_t>; // exposition only + + public: + using @\libmember{value_type}{expected}@ = T; + using @\libmember{error_type}{expected}@ = E; + using @\libmember{unexpected_type}{expected}@ = unexpected; + + template using rebind = expected; + + // ------------------------------------------------------------------------- + // [expected.object.cons] Constructors + // ------------------------------------------------------------------------- + + // Default constructor: value-initializes T + constexpr expected() noexcept(is_nothrow_default_constructible_v) + requires is_default_constructible_v; + + // Copy constructor (trivial path). Unconstrained on purpose: this is the + // sole declaration when T or E is not copy constructible at all (it is + // then implicitly defined as deleted), and the more-constrained + // non-trivial-path overload below is selected over it by constraint + // subsumption whenever it is viable. + constexpr expected(const expected&) = default; + + // Copy constructor (non-trivial path) + constexpr expected(const expected& rhs) noexcept(is_nothrow_copy_constructible_v && + is_nothrow_copy_constructible_v) + requires(is_copy_constructible_v && is_copy_constructible_v && + !(is_trivially_copy_constructible_v && + is_trivially_copy_constructible_v)); + + // Move constructor (trivial path). Unconstrained; see the copy + // constructor above for why. No explicit noexcept: let the compiler + // deduce it, so a non-movable-at-all E deletes rather than mismatches. + constexpr expected(expected&&) = default; + + // Move constructor (non-trivial path) + constexpr expected(expected&& rhs) noexcept(is_nothrow_move_constructible_v && + is_nothrow_move_constructible_v) + requires(is_move_constructible_v && is_move_constructible_v && + !(is_trivially_move_constructible_v && + is_trivially_move_constructible_v)); + + // Converting copy constructor from expected — value-E path + template + requires(!is_reference_v && is_constructible_v && + is_constructible_v && + (is_same_v> || + !@\exposidnc{converts-from-any-cvref}@>) && + !is_constructible_v, expected&> && + !is_constructible_v, expected&&> && + !is_constructible_v, const expected&> && + !is_constructible_v, const expected&&>) + constexpr explicit(!is_convertible_v || !is_convertible_v) + expected(const expected& rhs); + + // Converting move constructor from expected — value-E path + template + requires(!is_reference_v && is_constructible_v && + is_constructible_v && + (is_same_v> || + !@\exposidnc{converts-from-any-cvref}@>) && + !is_constructible_v, expected&> && + !is_constructible_v, expected&&> && + !is_constructible_v, const expected&> && + !is_constructible_v, const expected&&>) + constexpr explicit(!is_convertible_v || !is_convertible_v) + expected(expected&& rhs); + + // Converting constructor from expected — reference-E path: only accepts sources + // whose error type G is itself a reference convertible to E. + template + requires(is_reference_v && is_reference_v && + is_constructible_v && is_convertible_v) + constexpr explicit(!is_convertible_v || !is_convertible_v) + expected(const expected& rhs); + + template + requires(is_reference_v && is_reference_v && is_constructible_v && + is_convertible_v) + constexpr explicit(!is_convertible_v || !is_convertible_v) + expected(expected&& rhs); + + // Constructor from value U&& + template> + requires(!is_same_v, in_place_t> && + !is_same_v, unexpect_t> && + !is_same_v, expected> && is_constructible_v && + !@\exposidnc{is-unexpected-specialization}@>::value && + (!is_same_v> || + !@\exposidnc{is-expected-specialization}@>::value)) + constexpr explicit(!is_convertible_v) expected(U&& v); + + // Constructor from unexpected const& / && — value-E path + template + requires(!is_reference_v && is_constructible_v) + constexpr explicit(!is_convertible_v) expected(const unexpected& e); + + template + requires(!is_reference_v && is_constructible_v) + constexpr explicit(!is_convertible_v) expected(unexpected&& e); + + // Constructor from unexpected — reference-E path. Allowed only when G is itself a + // reference, i.e. the source unexpected holds a reference to an external object, so + // binding E& to e.error() cannot dangle regardless of the source's value category. No + // const_cast is needed. + template + requires(is_reference_v && is_reference_v && is_constructible_v && + !reference_constructs_from_temporary_v) + constexpr explicit(!is_convertible_v) expected(const unexpected& e) noexcept; + + template + requires(is_reference_v && is_reference_v && is_constructible_v && + !reference_constructs_from_temporary_v) + constexpr explicit(!is_convertible_v) expected(unexpected&& e) noexcept; + + // Deleted for reference E with value G: the referent lives inside the unexpected + // object, so binding E& to it would dangle once a temporary source is destroyed. Use + // (unexpect, lvalue), or an unexpected holding an external object, instead. + template + requires(is_reference_v && !is_reference_v) + constexpr expected(const unexpected&) = BEMAN_EXPECTED_DELETE_MSG( + "expected: cannot construct from unexpected; the value would dangle " + "— use unexpected"); + + template + requires(is_reference_v && !is_reference_v) + constexpr expected(unexpected&&) = BEMAN_EXPECTED_DELETE_MSG( + "expected: cannot construct from unexpected; the value would dangle " + "— use unexpected"); + + // In-place constructor for value + template + requires is_constructible_v + constexpr explicit expected(in_place_t, Args&&... args); + + // In-place constructor for value with initializer_list + template + requires is_constructible_v&, Args...> + constexpr explicit expected(in_place_t, initializer_list il, Args&&... args); + + // In-place constructor for error + template + requires(is_constructible_v && !@\exposidnc{unexpect-dangles-v}@) + constexpr explicit expected(unexpect_t, Args&&... args); + + // Deleted: single argument would bind E& to a temporary — dangling prevention + template + requires(@\exposidnc{unexpect-dangles-v}@) + constexpr expected(unexpect_t, Args&&...) = + BEMAN_EXPECTED_DELETE_MSG("expected: unexpect argument would bind a " + "temporary that dangles; pass an lvalue reference"); + + // Deleted catch-all: reference E, argument neither constructible nor a dangling case + // (e.g. binding a non-const E& from a const lvalue). + template + requires(is_reference_v && !is_constructible_v && + !@\exposidnc{unexpect-dangles-v}@) + constexpr expected(unexpect_t, Args&&...) = BEMAN_EXPECTED_DELETE_MSG( + "expected: no viable conversion from the given argument(s) to E&"); + + // In-place constructor for error with initializer_list + template + requires(!is_reference_v && is_constructible_v&, Args...>) + constexpr explicit expected(unexpect_t, initializer_list il, Args&&... args); + + template + requires is_reference_v + constexpr expected(unexpect_t, initializer_list, Args&&...) = + BEMAN_EXPECTED_DELETE_MSG("expected: initializer-list error construction " + "cannot bind a reference; pass an lvalue reference"); + + // ------------------------------------------------------------------------- + // [expected.object.dtor] Destructor + // ------------------------------------------------------------------------- + + constexpr ~expected() + requires(is_trivially_destructible_v && is_trivially_destructible_v) + = default; + + constexpr ~expected() + requires(!(is_trivially_destructible_v && is_trivially_destructible_v)); + + // ------------------------------------------------------------------------- + // [expected.object.assign] Assignment + // ------------------------------------------------------------------------- + + // Copy assignment (trivial path) + constexpr expected& operator=(const expected&) + requires(is_trivially_copy_constructible_v && is_trivially_copy_assignable_v && is_trivially_destructible_v && is_trivially_copy_constructible_v && - is_trivially_copy_assignable_v && is_trivially_destructible_v)); - - // Move assignment (trivial path) - constexpr expected& operator=(expected&&) noexcept - requires(is_trivially_move_constructible_v && - is_trivially_move_assignable_v && is_trivially_destructible_v && - is_trivially_move_constructible_v && - is_trivially_move_assignable_v && is_trivially_destructible_v) - = default; - - // Move assignment (non-trivial path) - constexpr expected& operator=(expected&& rhs) noexcept( - is_nothrow_move_constructible_v && is_nothrow_move_assignable_v && - is_nothrow_move_constructible_v && is_nothrow_move_assignable_v) - requires(is_move_constructible_v && is_move_assignable_v && - (is_reference_v || - (is_move_constructible_v && is_move_assignable_v)) && - (is_nothrow_move_constructible_v || - is_nothrow_move_constructible_v) && - !(is_trivially_move_constructible_v && + is_trivially_copy_assignable_v && is_trivially_destructible_v) + = default; + + // Copy assignment (non-trivial path) + constexpr expected& operator=(const expected& rhs) noexcept( + is_nothrow_copy_constructible_v && is_nothrow_copy_assignable_v && + is_nothrow_copy_constructible_v && is_nothrow_copy_assignable_v) + requires(is_copy_constructible_v && is_copy_assignable_v && + (is_reference_v || + (is_copy_constructible_v && is_copy_assignable_v)) && + (is_nothrow_move_constructible_v || + is_nothrow_move_constructible_v) && + !(is_trivially_copy_constructible_v && + is_trivially_copy_assignable_v && is_trivially_destructible_v && + is_trivially_copy_constructible_v && + is_trivially_copy_assignable_v && is_trivially_destructible_v)); + + // Move assignment (trivial path) + constexpr expected& operator=(expected&&) noexcept + requires(is_trivially_move_constructible_v && is_trivially_move_assignable_v && is_trivially_destructible_v && is_trivially_move_constructible_v && - is_trivially_move_assignable_v && is_trivially_destructible_v)); - - // Assignment from value U&& - template> - requires(!is_same_v> && - !@\exposidnc{is-unexpected-specialization}@>::value && - is_constructible_v && is_assignable_v && - (is_nothrow_constructible_v || is_nothrow_move_constructible_v || - is_nothrow_move_constructible_v)) - constexpr expected& operator=(U&& v); - - // Assignment from unexpected — value-E path - template - requires(!is_reference_v && is_constructible_v && - is_assignable_v && - (is_nothrow_constructible_v || - is_nothrow_move_constructible_v || is_nothrow_move_constructible_v)) - constexpr expected& operator=(const unexpected& e); - - template - requires(!is_reference_v && is_constructible_v && is_assignable_v && - (is_nothrow_constructible_v || is_nothrow_move_constructible_v || - is_nothrow_move_constructible_v)) - constexpr expected& operator=(unexpected&& e); - - // Rebinding assignment for reference E from reference G — binds unex_ to the external - // referent (never dangles; pointer store is noexcept). Mirrors the reference-E - // constructor. - template - requires(is_reference_v && is_reference_v && is_constructible_v && - !reference_constructs_from_temporary_v) - constexpr expected& operator=(const unexpected& e); - - template - requires(is_reference_v && is_reference_v && is_constructible_v && - !reference_constructs_from_temporary_v) - constexpr expected& operator=(unexpected&& e); - - // Deleted for reference E with value G: would rebind E& to unexpected's temporary - // storage. - template - requires(is_reference_v && !is_reference_v) - constexpr expected& operator=(const unexpected&) = - BEMAN_EXPECTED_DELETE_MSG("expected: cannot assign from unexpected; " - "the value would dangle — use unexpected"); - - template - requires(is_reference_v && !is_reference_v) - constexpr expected& operator=(unexpected&&) = - BEMAN_EXPECTED_DELETE_MSG("expected: cannot assign from unexpected; " - "the value would dangle — use unexpected"); - - // Emplace: destroy current value/error, construct value in-place - template - requires is_nothrow_constructible_v - constexpr T& emplace(Args&&... args) noexcept; - - template - requires is_nothrow_constructible_v&, Args...> - constexpr T& emplace(initializer_list il, Args&&... args) noexcept; - - // ------------------------------------------------------------------------- - // [expected.object.swap] Swap - // ------------------------------------------------------------------------- - - constexpr void swap(expected& rhs) noexcept(is_nothrow_move_constructible_v && - is_nothrow_swappable_v && - is_nothrow_move_constructible_v && - (is_reference_v || - is_nothrow_swappable_v)) - requires(is_swappable_v && (is_reference_v || is_swappable_v) && - is_move_constructible_v && is_move_constructible_v && - (is_nothrow_move_constructible_v || is_nothrow_move_constructible_v)) - ; - - friend constexpr void swap(expected& x, expected& y) noexcept(noexcept(x.swap(y))) - requires(is_swappable_v && (is_reference_v || is_swappable_v) && - is_move_constructible_v && is_move_constructible_v && - (is_nothrow_move_constructible_v || is_nothrow_move_constructible_v)) - ; - - // ------------------------------------------------------------------------- - // [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&; - constexpr T& value() &; - constexpr const 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 T value_or(U&& def) const&; - - template> constexpr T value_or(U&& def) &&; - - template - requires(is_copy_constructible_v<@\exposidnc{error-value-type}@> && - is_convertible_v) - constexpr @\exposidnc{error-value-type}@ error_or(G&& def) const&; - - template - requires(is_move_constructible_v<@\exposidnc{error-value-type}@> && - is_convertible_v) - constexpr @\exposidnc{error-value-type}@ error_or(G&& def) &&; - - // ------------------------------------------------------------------------- - // [expected.object.monadic] Monadic operations - // ------------------------------------------------------------------------- - - template - requires is_constructible_v - constexpr auto and_then(F&& f) &; - template - requires is_constructible_v - constexpr auto and_then(F&& f) &&; - template - requires is_constructible_v - constexpr auto and_then(F&& f) const&; - template - requires is_constructible_v - constexpr auto and_then(F&& f) const&&; - - template - requires is_constructible_v - constexpr auto or_else(F&& f) &; - template - requires is_constructible_v - constexpr auto or_else(F&& f) &&; - template - requires is_constructible_v - constexpr auto or_else(F&& f) const&; - template - requires is_constructible_v - constexpr auto or_else(F&& f) const&&; - - template - requires is_constructible_v - constexpr auto transform(F&& f) &; - template - requires is_constructible_v - constexpr auto transform(F&& f) &&; - template - requires is_constructible_v - constexpr auto transform(F&& f) const&; - template - requires is_constructible_v - constexpr auto transform(F&& f) const&&; - - template - requires is_constructible_v - constexpr auto transform_error(F&& f) &; - template - requires is_constructible_v - constexpr auto transform_error(F&& f) &&; - template - requires is_constructible_v - constexpr auto transform_error(F&& f) const&; - template - requires is_constructible_v - constexpr auto transform_error(F&& f) const&&; - - // ------------------------------------------------------------------------- - // [expected.object.eq] Equality operators (hidden friends) - // ------------------------------------------------------------------------- - - template - requires(!is_void_v) - friend constexpr bool operator==(const expected& x, const expected& y); - - template - requires(!@\exposidnc{is-expected-specialization}@::value) - friend constexpr bool operator==(const expected& x, const T2& val); - - template - friend constexpr bool operator==(const expected& x, const unexpected& e); - -private: - bool @\exposidnc{has-val}@; // exposition only - union { - T @\exposidnc{val}@; // exposition only - unexpected @\exposidnc{unex}@; // exposition only + is_trivially_move_assignable_v && is_trivially_destructible_v) + = default; + + // Move assignment (non-trivial path) + constexpr expected& operator=(expected&& rhs) noexcept( + is_nothrow_move_constructible_v && is_nothrow_move_assignable_v && + is_nothrow_move_constructible_v && is_nothrow_move_assignable_v) + requires(is_move_constructible_v && is_move_assignable_v && + (is_reference_v || + (is_move_constructible_v && is_move_assignable_v)) && + (is_nothrow_move_constructible_v || + is_nothrow_move_constructible_v) && + !(is_trivially_move_constructible_v && + is_trivially_move_assignable_v && is_trivially_destructible_v && + is_trivially_move_constructible_v && + is_trivially_move_assignable_v && is_trivially_destructible_v)); + + // Assignment from value U&& + template> + requires(!is_same_v> && + !@\exposidnc{is-unexpected-specialization}@>::value && + is_constructible_v && is_assignable_v && + (is_nothrow_constructible_v || is_nothrow_move_constructible_v || + is_nothrow_move_constructible_v)) + constexpr expected& operator=(U&& v); + + // Assignment from unexpected — value-E path + template + requires(!is_reference_v && is_constructible_v && + is_assignable_v && + (is_nothrow_constructible_v || + is_nothrow_move_constructible_v || is_nothrow_move_constructible_v)) + constexpr expected& operator=(const unexpected& e); + + template + requires(!is_reference_v && is_constructible_v && is_assignable_v && + (is_nothrow_constructible_v || is_nothrow_move_constructible_v || + is_nothrow_move_constructible_v)) + constexpr expected& operator=(unexpected&& e); + + // Rebinding assignment for reference E from reference G — binds unex_ to the external + // referent (never dangles; pointer store is noexcept). Mirrors the reference-E + // constructor. + template + requires(is_reference_v && is_reference_v && is_constructible_v && + !reference_constructs_from_temporary_v) + constexpr expected& operator=(const unexpected& e); + + template + requires(is_reference_v && is_reference_v && is_constructible_v && + !reference_constructs_from_temporary_v) + constexpr expected& operator=(unexpected&& e); + + // Deleted for reference E with value G: would rebind E& to unexpected's temporary + // storage. + template + requires(is_reference_v && !is_reference_v) + constexpr expected& operator=(const unexpected&) = + BEMAN_EXPECTED_DELETE_MSG("expected: cannot assign from unexpected; " + "the value would dangle — use unexpected"); + + template + requires(is_reference_v && !is_reference_v) + constexpr expected& operator=(unexpected&&) = + BEMAN_EXPECTED_DELETE_MSG("expected: cannot assign from unexpected; " + "the value would dangle — use unexpected"); + + // Emplace: destroy current value/error, construct value in-place + template + requires is_nothrow_constructible_v + constexpr T& emplace(Args&&... args) noexcept; + + template + requires is_nothrow_constructible_v&, Args...> + constexpr T& emplace(initializer_list il, Args&&... args) noexcept; + + // ------------------------------------------------------------------------- + // [expected.object.swap] Swap + // ------------------------------------------------------------------------- + + constexpr void swap(expected& rhs) noexcept(is_nothrow_move_constructible_v && + is_nothrow_swappable_v && + is_nothrow_move_constructible_v && + (is_reference_v || + is_nothrow_swappable_v)) + requires(is_swappable_v && (is_reference_v || is_swappable_v) && + is_move_constructible_v && is_move_constructible_v && + (is_nothrow_move_constructible_v || is_nothrow_move_constructible_v)) + ; + + friend constexpr void swap(expected& x, expected& y) noexcept(noexcept(x.swap(y))) + requires(is_swappable_v && (is_reference_v || is_swappable_v) && + is_move_constructible_v && is_move_constructible_v && + (is_nothrow_move_constructible_v || is_nothrow_move_constructible_v)) + ; + + // ------------------------------------------------------------------------- + // [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&; + constexpr T& value() &; + constexpr const 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 T value_or(U&& def) const&; + + template> constexpr T value_or(U&& def) &&; + + template + requires(is_copy_constructible_v<@\exposidnc{error-value-type}@> && + is_convertible_v) + constexpr @\exposidnc{error-value-type}@ error_or(G&& def) const&; + + template + requires(is_move_constructible_v<@\exposidnc{error-value-type}@> && + is_convertible_v) + constexpr @\exposidnc{error-value-type}@ error_or(G&& def) &&; + + // ------------------------------------------------------------------------- + // [expected.object.monadic] Monadic operations + // ------------------------------------------------------------------------- + + template + requires is_constructible_v + constexpr auto and_then(F&& f) &; + template + requires is_constructible_v + constexpr auto and_then(F&& f) &&; + template + requires is_constructible_v + constexpr auto and_then(F&& f) const&; + template + requires is_constructible_v + constexpr auto and_then(F&& f) const&&; + + template + requires is_constructible_v + constexpr auto or_else(F&& f) &; + template + requires is_constructible_v + constexpr auto or_else(F&& f) &&; + template + requires is_constructible_v + constexpr auto or_else(F&& f) const&; + template + requires is_constructible_v + constexpr auto or_else(F&& f) const&&; + + template + requires is_constructible_v + constexpr auto transform(F&& f) &; + template + requires is_constructible_v + constexpr auto transform(F&& f) &&; + template + requires is_constructible_v + constexpr auto transform(F&& f) const&; + template + requires is_constructible_v + constexpr auto transform(F&& f) const&&; + + template + requires is_constructible_v + constexpr auto transform_error(F&& f) &; + template + requires is_constructible_v + constexpr auto transform_error(F&& f) &&; + template + requires is_constructible_v + constexpr auto transform_error(F&& f) const&; + template + requires is_constructible_v + constexpr auto transform_error(F&& f) const&&; + + // ------------------------------------------------------------------------- + // [expected.object.eq] Equality operators (hidden friends) + // ------------------------------------------------------------------------- + + template + requires(!is_void_v) + friend constexpr bool operator==(const expected& x, const expected& y); + + template + requires(!@\exposidnc{is-expected-specialization}@::value) + friend constexpr bool operator==(const expected& x, const T2& val); + + template + friend constexpr bool operator==(const expected& x, const unexpected& e); + + private: + bool @\exposidnc{has-val}@; // exposition only + union { + T @\exposidnc{val}@; // exposition only + unexpected @\exposidnc{unex}@; // exposition only + }; }; -}; +} \end{codeblock} \begin{itemdescr} @@ -2092,326 +2102,327 @@ \rSec3[expected.void.general]{General} \begin{codeblock} -template -class @\libglobal{expected}@ { -private: - using @\exposidnc{error-value-type}@ = remove_cv_t>; // exposition only - -public: - using @\libmember{value_type}{expected}@ = void; - using @\libmember{error_type}{expected}@ = E; - using @\libmember{unexpected_type}{expected}@ = unexpected; - - template using rebind = expected; - - // ------------------------------------------------------------------------- - // [expected.void.cons] Constructors - // ------------------------------------------------------------------------- - - constexpr expected() noexcept; - - // Unconstrained trivial-path candidate: see the primary template's copy - // constructor for why (the sole declaration when E is not copy - // constructible at all; subsumed by the non-trivial path otherwise). - - constexpr expected(const expected& rhs) noexcept(is_nothrow_copy_constructible_v) - requires(is_copy_constructible_v && !is_trivially_copy_constructible_v); - - // Unconstrained; no explicit noexcept — see the primary template's move - // constructor for why. - - constexpr expected(expected&& rhs) noexcept(is_nothrow_move_constructible_v) - requires(is_move_constructible_v && !is_trivially_move_constructible_v); - - // Converting constructor from expected where is_void_v. Excludes U,G exactly - // matching this class's own void,E (the real copy/move constructors already handle - // that case) — instantiating this template for the self-referential case would - // otherwise probe unexpected's constructibility from this very class, which some - // standard library implementations of reference_constructs_from_temporary_v resolve - // as a circular constraint. - template - requires(is_void_v && !is_reference_v && !is_same_v && - is_constructible_v && - !is_constructible_v, expected&> && - !is_constructible_v, expected&&> && - !is_constructible_v, const expected&> && - !is_constructible_v, const expected&&>) - constexpr explicit(!is_convertible_v) - expected(const expected& rhs); - - template - requires(is_void_v && !is_reference_v && !is_same_v && - is_constructible_v && - !is_constructible_v, expected&> && - !is_constructible_v, expected&&> && - !is_constructible_v, const expected&> && - !is_constructible_v, const expected&&>) - constexpr explicit(!is_convertible_v) expected(expected&& rhs); - - // Constructor from unexpected const& / && — value-E path - template - requires(!is_reference_v && is_constructible_v) - constexpr explicit(!is_convertible_v) expected(const unexpected& e); - - template - requires(!is_reference_v && is_constructible_v) - constexpr explicit(!is_convertible_v) expected(unexpected&& e); - - // Constructor from unexpected — reference-E path. Allowed only when G is itself a - // reference, so e.error() refers to an external object and binding E& cannot dangle. - // No const_cast needed. - template - requires(is_reference_v && is_reference_v && is_constructible_v && - !reference_constructs_from_temporary_v) - constexpr explicit(!is_convertible_v) expected(const unexpected& e) noexcept; - - template - requires(is_reference_v && is_reference_v && is_constructible_v && - !reference_constructs_from_temporary_v) - constexpr explicit(!is_convertible_v) expected(unexpected&& e) noexcept; - - // Deleted for reference E with value G: the referent lives inside the temporary - // unexpected, so binding E& to it would dangle once the source is destroyed. Use - // (unexpect, lvalue) instead. - template - requires(is_reference_v && !is_reference_v) - constexpr expected(const unexpected&) = BEMAN_EXPECTED_DELETE_MSG( - "expected: cannot construct from unexpected; the value would " - "dangle — use unexpected"); - - template - requires(is_reference_v && !is_reference_v) - constexpr expected(unexpected&&) = BEMAN_EXPECTED_DELETE_MSG( - "expected: cannot construct from unexpected; the value would " - "dangle — use unexpected"); - - // In-place constructor for value (no args, just marks has-value) - constexpr explicit expected(in_place_t) noexcept; - - // In-place constructor for error - template - requires(is_constructible_v && !@\exposidnc{unexpect-dangles-v}@) - constexpr explicit expected(unexpect_t, Args&&... args); - - // Deleted: single argument would bind E& to a temporary — dangling prevention - template - requires(@\exposidnc{unexpect-dangles-v}@) - constexpr expected(unexpect_t, Args&&...) = - BEMAN_EXPECTED_DELETE_MSG("expected: unexpect argument would bind a " - "temporary that dangles; pass an lvalue reference"); - - // Deleted catch-all: reference E, argument neither constructible nor a dangling case - // (e.g. binding a non-const E& from a const lvalue). - template - requires(is_reference_v && !is_constructible_v && - !@\exposidnc{unexpect-dangles-v}@) - constexpr expected(unexpect_t, Args&&...) = BEMAN_EXPECTED_DELETE_MSG( - "expected: no viable conversion from the given argument(s) to E&"); - - // In-place constructor for error with initializer_list - template - requires(!is_reference_v && is_constructible_v&, Args...>) - constexpr explicit expected(unexpect_t, initializer_list il, Args&&... args); - - template - requires is_reference_v - constexpr expected(unexpect_t, initializer_list, Args&&...) = - BEMAN_EXPECTED_DELETE_MSG( - "expected: initializer-list error construction cannot bind a " - "reference; pass an lvalue reference"); - - // Converting constructor from expected — reference-E path only. G is itself - // a reference to an external object, so binding E& to it cannot dangle regardless of - // the source's value category, provided the reference conversion itself does not - // materialize a temporary (e.g. a base-from-derived or qualification conversion is - // fine; a user-defined conversion that returns by value is not). Mirrors the - // unexpected reference-E path above. - template - requires(is_reference_v && is_convertible_v && - !reference_constructs_from_temporary_v) - constexpr explicit(!is_convertible_v) expected(const expected& rhs); - - template - requires(is_reference_v && is_convertible_v && - !reference_constructs_from_temporary_v) - constexpr explicit(!is_convertible_v) expected(expected&& rhs); - - // ------------------------------------------------------------------------- - // [expected.void.dtor] Destructor - // ------------------------------------------------------------------------- - - constexpr ~expected() - requires(!is_trivially_destructible_v); - - // ------------------------------------------------------------------------- - // [expected.void.assign] Assignment - // ------------------------------------------------------------------------- - - // Copy assignment (trivial path) - - // Copy assignment (non-trivial path) - constexpr expected& operator=(const expected& rhs) noexcept( - is_nothrow_copy_constructible_v && is_nothrow_copy_assignable_v) - requires((is_reference_v || - (is_copy_constructible_v && is_copy_assignable_v)) && - !(is_trivially_copy_constructible_v && - is_trivially_copy_assignable_v && is_trivially_destructible_v)); - - // Move assignment (trivial path) - - // Move assignment (non-trivial path) - constexpr expected& operator=(expected&& rhs) noexcept( - is_nothrow_move_constructible_v && is_nothrow_move_assignable_v) - requires((is_reference_v || - (is_move_constructible_v && is_move_assignable_v)) && - !(is_trivially_move_constructible_v && - is_trivially_move_assignable_v && is_trivially_destructible_v)); - - // Assignment from unexpected — value-E path. - template - requires(!is_reference_v && is_constructible_v && - is_assignable_v) - constexpr expected& operator=(const unexpected& e); - - template - requires(!is_reference_v && is_constructible_v && is_assignable_v) - constexpr expected& operator=(unexpected&& e); - - // Rebinding assignment for reference E from reference G — binds unex_ to the external - // referent. - template - requires(is_reference_v && is_reference_v && is_constructible_v && - !reference_constructs_from_temporary_v) - constexpr expected& operator=(const unexpected& e); - - template - requires(is_reference_v && is_reference_v && is_constructible_v && - !reference_constructs_from_temporary_v) - constexpr expected& operator=(unexpected&& e); - - // Deleted for reference E with value G: would bind E& to storage inside the temporary - // unexpected. - template - requires(is_reference_v && !is_reference_v) - constexpr expected& operator=(const unexpected&) = BEMAN_EXPECTED_DELETE_MSG( - "expected: cannot assign from unexpected; the value would dangle " - "— use unexpected"); - - template - requires(is_reference_v && !is_reference_v) - constexpr expected& operator=(unexpected&&) = BEMAN_EXPECTED_DELETE_MSG( - "expected: cannot assign from unexpected; the value would dangle " - "— use unexpected"); - - constexpr void emplace() noexcept; - - // ------------------------------------------------------------------------- - // [expected.void.swap] Swap - // ------------------------------------------------------------------------- - - constexpr void swap(expected& rhs) noexcept(is_nothrow_move_constructible_v && - (is_reference_v || - is_nothrow_swappable_v)) - requires((is_reference_v || is_swappable_v) && is_move_constructible_v); - - friend constexpr void swap(expected& x, expected& y) noexcept(noexcept(x.swap(y))) - requires((is_reference_v || is_swappable_v) && is_move_constructible_v); - - // ------------------------------------------------------------------------- - // [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&; - constexpr void value() &&; - - // error() — shallow const for reference E: always returns E& regardless of const on - // expected - constexpr const E& error() const& noexcept; - constexpr E& error() & noexcept; - constexpr const E&& error() const&& noexcept; - constexpr E&& error() && noexcept; - - template - requires(is_copy_constructible_v>> && - is_convertible_v>>) - constexpr @\exposidnc{error-value-type}@ error_or(G&& def) const&; - - template - requires(is_move_constructible_v>> && - is_convertible_v>>) - constexpr @\exposidnc{error-value-type}@ error_or(G&& def) &&; - - // Deleted: value_or is not available for void expected. Gated to reference E only so - // that, for value E, no value_or overload is declared at all (there is nothing to - // delete against). - template - requires is_reference_v - constexpr void value_or(U&&) const = BEMAN_EXPECTED_DELETE_MSG( - "expected: value_or is not defined for void value_type; there is no " - "value to fall back from — use has_value()/error()"); - - // ------------------------------------------------------------------------- - // [expected.void.monadic] Monadic operations - // ------------------------------------------------------------------------- - - template - requires is_constructible_v - constexpr auto and_then(F&& f) &; - template - requires is_constructible_v - constexpr auto and_then(F&& f) &&; - template - requires is_constructible_v - constexpr auto and_then(F&& f) const&; - template - requires is_constructible_v - 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 - requires is_constructible_v - constexpr auto transform(F&& f) &; - template - requires is_constructible_v - constexpr auto transform(F&& f) &&; - template - requires is_constructible_v - constexpr auto transform(F&& f) const&; - template - requires is_constructible_v - 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&&; - - // ------------------------------------------------------------------------- - // [expected.void.eq] Equality operators (hidden friends) - // ------------------------------------------------------------------------- - - template - requires is_void_v - friend constexpr bool operator==(const expected& x, const expected& y); - - template - friend constexpr bool operator==(const expected& x, const unexpected& e); - -private: - bool @\exposidnc{has-val}@; // exposition only - union { - unexpected @\exposidnc{unex}@; // exposition only +namespace std { + template + class @\libglobal{expected}@ { + private: + using @\exposidnc{error-value-type}@ = remove_cv_t>; // exposition only + + public: + using @\libmember{value_type}{expected}@ = void; + using @\libmember{error_type}{expected}@ = E; + using @\libmember{unexpected_type}{expected}@ = unexpected; + + template using rebind = expected; + + // ------------------------------------------------------------------------- + // [expected.void.cons] Constructors + // ------------------------------------------------------------------------- + + constexpr expected() noexcept; + + // Unconstrained trivial-path candidate: see the primary template's copy + // constructor for why (the sole declaration when E is not copy + // constructible at all; subsumed by the non-trivial path otherwise). + + constexpr expected(const expected& rhs) noexcept(is_nothrow_copy_constructible_v) + requires(is_copy_constructible_v && !is_trivially_copy_constructible_v); + + // Unconstrained; no explicit noexcept — see the primary template's move + // constructor for why. + + constexpr expected(expected&& rhs) noexcept(is_nothrow_move_constructible_v) + requires(is_move_constructible_v && !is_trivially_move_constructible_v); + + // Converting constructor from expected where is_void_v. Excludes U,G exactly + // matching this class's own void,E (the real copy/move constructors already handle + // that case) — instantiating this template for the self-referential case would + // otherwise probe unexpected's constructibility from this very class, which some + // standard library implementations of reference_constructs_from_temporary_v resolve + // as a circular constraint. + template + requires(is_void_v && !is_reference_v && !is_same_v && + is_constructible_v && + !is_constructible_v, expected&> && + !is_constructible_v, expected&&> && + !is_constructible_v, const expected&> && + !is_constructible_v, const expected&&>) + constexpr explicit(!is_convertible_v) + expected(const expected& rhs); + + template + requires(is_void_v && !is_reference_v && !is_same_v && + is_constructible_v && + !is_constructible_v, expected&> && + !is_constructible_v, expected&&> && + !is_constructible_v, const expected&> && + !is_constructible_v, const expected&&>) + constexpr explicit(!is_convertible_v) expected(expected&& rhs); + + // Constructor from unexpected const& / && — value-E path + template + requires(!is_reference_v && is_constructible_v) + constexpr explicit(!is_convertible_v) expected(const unexpected& e); + + template + requires(!is_reference_v && is_constructible_v) + constexpr explicit(!is_convertible_v) expected(unexpected&& e); + + // Constructor from unexpected — reference-E path. Allowed only when G is itself a + // reference, so e.error() refers to an external object and binding E& cannot dangle. + // No const_cast needed. + template + requires(is_reference_v && is_reference_v && is_constructible_v && + !reference_constructs_from_temporary_v) + constexpr explicit(!is_convertible_v) expected(const unexpected& e) noexcept; + + template + requires(is_reference_v && is_reference_v && is_constructible_v && + !reference_constructs_from_temporary_v) + constexpr explicit(!is_convertible_v) expected(unexpected&& e) noexcept; + + // Deleted for reference E with value G: the referent lives inside the temporary + // unexpected, so binding E& to it would dangle once the source is destroyed. Use + // (unexpect, lvalue) instead. + template + requires(is_reference_v && !is_reference_v) + constexpr expected(const unexpected&) = BEMAN_EXPECTED_DELETE_MSG( + "expected: cannot construct from unexpected; the value would " + "dangle — use unexpected"); + + template + requires(is_reference_v && !is_reference_v) + constexpr expected(unexpected&&) = BEMAN_EXPECTED_DELETE_MSG( + "expected: cannot construct from unexpected; the value would " + "dangle — use unexpected"); + + // In-place constructor for value (no args, just marks has-value) + constexpr explicit expected(in_place_t) noexcept; + + // In-place constructor for error + template + requires(is_constructible_v && !@\exposidnc{unexpect-dangles-v}@) + constexpr explicit expected(unexpect_t, Args&&... args); + + // Deleted: single argument would bind E& to a temporary — dangling prevention + template + requires(@\exposidnc{unexpect-dangles-v}@) + constexpr expected(unexpect_t, Args&&...) = + BEMAN_EXPECTED_DELETE_MSG("expected: unexpect argument would bind a " + "temporary that dangles; pass an lvalue reference"); + + // Deleted catch-all: reference E, argument neither constructible nor a dangling case + // (e.g. binding a non-const E& from a const lvalue). + template + requires(is_reference_v && !is_constructible_v && + !@\exposidnc{unexpect-dangles-v}@) + constexpr expected(unexpect_t, Args&&...) = BEMAN_EXPECTED_DELETE_MSG( + "expected: no viable conversion from the given argument(s) to E&"); + + // In-place constructor for error with initializer_list + template + requires(!is_reference_v && is_constructible_v&, Args...>) + constexpr explicit expected(unexpect_t, initializer_list il, Args&&... args); + + template + requires is_reference_v + constexpr expected(unexpect_t, initializer_list, Args&&...) = + BEMAN_EXPECTED_DELETE_MSG( + "expected: initializer-list error construction cannot bind a " + "reference; pass an lvalue reference"); + + // Converting constructor from expected — reference-E path only. G is itself + // a reference to an external object, so binding E& to it cannot dangle regardless of + // the source's value category, provided the reference conversion itself does not + // materialize a temporary (e.g. a base-from-derived or qualification conversion is + // fine; a user-defined conversion that returns by value is not). Mirrors the + // unexpected reference-E path above. + template + requires(is_reference_v && is_convertible_v && + !reference_constructs_from_temporary_v) + constexpr explicit(!is_convertible_v) expected(const expected& rhs); + + template + requires(is_reference_v && is_convertible_v && + !reference_constructs_from_temporary_v) + constexpr explicit(!is_convertible_v) expected(expected&& rhs); + + // ------------------------------------------------------------------------- + // [expected.void.dtor] Destructor + // ------------------------------------------------------------------------- + + constexpr ~expected(); + + // ------------------------------------------------------------------------- + // [expected.void.assign] Assignment + // ------------------------------------------------------------------------- + + // Copy assignment (trivial path) + + // Copy assignment (non-trivial path) + constexpr expected& operator=(const expected& rhs) noexcept( + is_nothrow_copy_constructible_v && is_nothrow_copy_assignable_v) + requires((is_reference_v || + (is_copy_constructible_v && is_copy_assignable_v)) && + !(is_trivially_copy_constructible_v && + is_trivially_copy_assignable_v && is_trivially_destructible_v)); + + // Move assignment (trivial path) + + // Move assignment (non-trivial path) + constexpr expected& operator=(expected&& rhs) noexcept( + is_nothrow_move_constructible_v && is_nothrow_move_assignable_v) + requires((is_reference_v || + (is_move_constructible_v && is_move_assignable_v)) && + !(is_trivially_move_constructible_v && + is_trivially_move_assignable_v && is_trivially_destructible_v)); + + // Assignment from unexpected — value-E path. + template + requires(!is_reference_v && is_constructible_v && + is_assignable_v) + constexpr expected& operator=(const unexpected& e); + + template + requires(!is_reference_v && is_constructible_v && is_assignable_v) + constexpr expected& operator=(unexpected&& e); + + // Rebinding assignment for reference E from reference G — binds unex_ to the external + // referent. + template + requires(is_reference_v && is_reference_v && is_constructible_v && + !reference_constructs_from_temporary_v) + constexpr expected& operator=(const unexpected& e); + + template + requires(is_reference_v && is_reference_v && is_constructible_v && + !reference_constructs_from_temporary_v) + constexpr expected& operator=(unexpected&& e); + + // Deleted for reference E with value G: would bind E& to storage inside the temporary + // unexpected. + template + requires(is_reference_v && !is_reference_v) + constexpr expected& operator=(const unexpected&) = BEMAN_EXPECTED_DELETE_MSG( + "expected: cannot assign from unexpected; the value would dangle " + "— use unexpected"); + + template + requires(is_reference_v && !is_reference_v) + constexpr expected& operator=(unexpected&&) = BEMAN_EXPECTED_DELETE_MSG( + "expected: cannot assign from unexpected; the value would dangle " + "— use unexpected"); + + constexpr void emplace() noexcept; + + // ------------------------------------------------------------------------- + // [expected.void.swap] Swap + // ------------------------------------------------------------------------- + + constexpr void swap(expected& rhs) noexcept(is_nothrow_move_constructible_v && + (is_reference_v || + is_nothrow_swappable_v)) + requires((is_reference_v || is_swappable_v) && is_move_constructible_v); + + friend constexpr void swap(expected& x, expected& y) noexcept(noexcept(x.swap(y))) + requires((is_reference_v || is_swappable_v) && is_move_constructible_v); + + // ------------------------------------------------------------------------- + // [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&; + constexpr void value() &&; + + // error() — shallow const for reference E: always returns E& regardless of const on + // expected + constexpr const E& error() const& noexcept; + constexpr E& error() & noexcept; + constexpr const E&& error() const&& noexcept; + constexpr E&& error() && noexcept; + + template + requires(is_copy_constructible_v>> && + is_convertible_v>>) + constexpr @\exposidnc{error-value-type}@ error_or(G&& def) const&; + + template + requires(is_move_constructible_v>> && + is_convertible_v>>) + constexpr @\exposidnc{error-value-type}@ error_or(G&& def) &&; + + // Deleted: value_or is not available for void expected. Gated to reference E only so + // that, for value E, no value_or overload is declared at all (there is nothing to + // delete against). + template + requires is_reference_v + constexpr void value_or(U&&) const = BEMAN_EXPECTED_DELETE_MSG( + "expected: value_or is not defined for void value_type; there is no " + "value to fall back from — use has_value()/error()"); + + // ------------------------------------------------------------------------- + // [expected.void.monadic] Monadic operations + // ------------------------------------------------------------------------- + + template + requires is_constructible_v + constexpr auto and_then(F&& f) &; + template + requires is_constructible_v + constexpr auto and_then(F&& f) &&; + template + requires is_constructible_v + constexpr auto and_then(F&& f) const&; + template + requires is_constructible_v + 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 + requires is_constructible_v + constexpr auto transform(F&& f) &; + template + requires is_constructible_v + constexpr auto transform(F&& f) &&; + template + requires is_constructible_v + constexpr auto transform(F&& f) const&; + template + requires is_constructible_v + 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&&; + + // ------------------------------------------------------------------------- + // [expected.void.eq] Equality operators (hidden friends) + // ------------------------------------------------------------------------- + + template + requires is_void_v + friend constexpr bool operator==(const expected& x, const expected& y); + + template + friend constexpr bool operator==(const expected& x, const unexpected& e); + + private: + bool @\exposidnc{has-val}@; // exposition only + union { + unexpected @\exposidnc{unex}@; // exposition only + }; }; -}; +} \end{codeblock} \begin{itemdescr} @@ -2711,10 +2722,6 @@ \end{itemdecl} \begin{itemdescr} -\pnum -\constraints -\tcode{is_trivially_destructible_v} is \tcode{false}. - \pnum \effects If \tcode{has_value()} is \tcode{false}, destroys \tcode{unex}. @@ -3221,363 +3228,365 @@ \rSec3[expected.ref.general]{General} \begin{codeblock} -template -class @\libglobal{expected}@ { -private: - using @\exposidnc{error-value-type}@ = remove_cv_t>; // exposition only - -public: - using @\libmember{value_type}{expected}@ = T&; - using @\libmember{error_type}{expected}@ = E; - using @\libmember{unexpected_type}{expected}@ = unexpected; - - template using rebind = expected; - - // ------------------------------------------------------------------------- - // Constructors - // ------------------------------------------------------------------------- - - expected() = BEMAN_EXPECTED_DELETE_MSG( - "expected: no default constructor; T& cannot be null"); - - // Copy constructor (trivial path). Unconstrained; see the primary - // template's copy constructor for why. - constexpr expected(const expected&) = default; - - // Copy constructor (non-trivial path) - constexpr expected(const expected& rhs) noexcept(is_nothrow_copy_constructible_v) - requires(is_copy_constructible_v && !is_trivially_copy_constructible_v); - - // Move constructor (trivial path). Unconstrained; no explicit noexcept. - constexpr expected(expected&&) = default; - - // Move constructor (non-trivial path) - constexpr expected(expected&& rhs) noexcept(is_nothrow_move_constructible_v) - requires(is_move_constructible_v && !is_trivially_move_constructible_v); - - // Deleted: no in-place value constructor — T& cannot be constructed in-place - template - constexpr expected(in_place_t, Args&&...) = BEMAN_EXPECTED_DELETE_MSG( - "expected: no in-place value constructor; T& cannot be constructed " - "in-place — pass a U convertible to T&"); - - // Value constructor — takes U that can bind to T& - template - requires(!is_same_v, in_place_t> && - !is_same_v, expected> && - !@\exposidnc{is-unexpected-specialization}@>::value && - is_constructible_v && !reference_constructs_from_temporary_v) - constexpr explicit(!is_convertible_v) - expected(U&& u) noexcept(is_nothrow_constructible_v); - - // Deleted: binding a temporary to T& creates a dangling reference - template - requires(reference_constructs_from_temporary_v) - constexpr expected(U&&) = - BEMAN_EXPECTED_DELETE_MSG("expected: argument would bind a temporary that " - "dangles; pass an lvalue reference"); - - // Converting constructor from expected (copy) — value-E path - template - requires(!is_reference_v && is_constructible_v && - is_constructible_v && - !reference_constructs_from_temporary_v) - constexpr explicit(!is_convertible_v || !is_convertible_v) - expected(const expected& rhs); - - // Converting constructor from expected (move) — value-E path - template - requires(!is_reference_v && is_constructible_v && - is_constructible_v && !reference_constructs_from_temporary_v) - constexpr explicit(!is_convertible_v || !is_convertible_v) - expected(expected&& rhs); - - // Converting constructor from expected (copy/move) — reference-E path: only - // accepts sources whose error type G is itself a reference convertible to E. - template - requires(is_reference_v && is_reference_v && is_constructible_v && - is_convertible_v && !reference_constructs_from_temporary_v) - constexpr explicit(!is_convertible_v || !is_convertible_v) - expected(const expected& rhs); - - template - requires(is_reference_v && is_reference_v && is_constructible_v && - is_convertible_v && !reference_constructs_from_temporary_v) - constexpr explicit(!is_convertible_v || !is_convertible_v) - expected(expected&& rhs); - - // Constructor from unexpected const& / && — value-E path - template - requires(!is_reference_v && is_constructible_v) - constexpr explicit(!is_convertible_v) expected(const unexpected& e); - - template - requires(!is_reference_v && is_constructible_v) - constexpr explicit(!is_convertible_v) expected(unexpected&& e); - - // Constructor from unexpected — reference-E path. Allowed only when G is itself a - // reference, i.e. the source unexpected holds a reference to an external object, so - // binding E& to e.error() cannot dangle regardless of the source's value category. No - // const_cast is needed. - template - requires(is_reference_v && is_reference_v && is_constructible_v && - !reference_constructs_from_temporary_v) - constexpr explicit(!is_convertible_v) expected(const unexpected& e) noexcept; - - template - requires(is_reference_v && is_reference_v && is_constructible_v && - !reference_constructs_from_temporary_v) - constexpr explicit(!is_convertible_v) expected(unexpected&& e) noexcept; - - // Deleted for reference E with value G: the referent lives inside the unexpected - // object, so binding E& to it would dangle once a temporary source is destroyed. Use - // (unexpect, lvalue), or an unexpected holding an external object, instead. - template - requires(is_reference_v && !is_reference_v) - constexpr expected(const unexpected&) = BEMAN_EXPECTED_DELETE_MSG( - "expected: cannot construct from unexpected; the value would " - "dangle — use unexpected"); - - template - requires(is_reference_v && !is_reference_v) - constexpr expected(unexpected&&) = BEMAN_EXPECTED_DELETE_MSG( - "expected: cannot construct from unexpected; the value would " - "dangle — use unexpected"); - - // In-place constructor for error - template - requires(is_constructible_v && !@\exposidnc{unexpect-dangles-v}@) - constexpr explicit expected(unexpect_t, Args&&... args); - - // Deleted: single argument would bind E& to a temporary — dangling prevention - template - requires(@\exposidnc{unexpect-dangles-v}@) - constexpr expected(unexpect_t, Args&&...) = - BEMAN_EXPECTED_DELETE_MSG("expected: unexpect argument would bind a " - "temporary that dangles; pass an lvalue reference"); - - // Deleted catch-all: reference E, argument neither constructible nor a dangling case - template - requires(is_reference_v && !is_constructible_v && - !@\exposidnc{unexpect-dangles-v}@) - constexpr expected(unexpect_t, Args&&...) = BEMAN_EXPECTED_DELETE_MSG( - "expected: no viable conversion from the given argument(s) to E&"); - - // In-place constructor for error with initializer_list - template - requires(!is_reference_v && is_constructible_v&, Args...>) - constexpr explicit expected(unexpect_t, initializer_list il, Args&&... args); - - template - requires is_reference_v - constexpr expected(unexpect_t, initializer_list, Args&&...) = - BEMAN_EXPECTED_DELETE_MSG("expected: initializer-list error construction " - "cannot bind a reference; pass an lvalue reference"); - - // ------------------------------------------------------------------------- - // Destructor - // ------------------------------------------------------------------------- - - constexpr ~expected() - requires is_trivially_destructible_v - = default; - - constexpr ~expected() - requires(!is_trivially_destructible_v); - - // ------------------------------------------------------------------------- - // Assignment (rebind semantics) - // ------------------------------------------------------------------------- - - // Copy assignment (trivial path) - constexpr expected& operator=(const expected&) - requires(is_trivially_copy_constructible_v && - is_trivially_copy_assignable_v && is_trivially_destructible_v) - = default; - - // Copy assignment (non-trivial path) - constexpr expected& operator=(const expected& rhs) noexcept( - is_nothrow_copy_constructible_v && is_nothrow_copy_assignable_v) - requires((is_reference_v || - (is_copy_constructible_v && is_copy_assignable_v)) && - !(is_trivially_copy_constructible_v && - is_trivially_copy_assignable_v && is_trivially_destructible_v)); - - // Move assignment (trivial path) - constexpr expected& operator=(expected&&) noexcept - requires(is_trivially_move_constructible_v && - is_trivially_move_assignable_v && is_trivially_destructible_v) - = default; - - // Move assignment (non-trivial path) - constexpr expected& operator=(expected&& rhs) noexcept( - is_nothrow_move_constructible_v && is_nothrow_move_assignable_v) - requires((is_reference_v || - (is_move_constructible_v && is_move_assignable_v)) && - !(is_trivially_move_constructible_v && - is_trivially_move_assignable_v && is_trivially_destructible_v)); - - // Rebind reference from lvalue - template - requires(!is_same_v, expected> && - !@\exposidnc{is-unexpected-specialization}@>::value && - is_constructible_v && !reference_constructs_from_temporary_v) - constexpr expected& operator=(U&& u); - - // Assignment from unexpected — value-E path - template - requires(!is_reference_v && is_constructible_v && - is_assignable_v) - constexpr expected& operator=(const unexpected& e); - - template - requires(!is_reference_v && is_constructible_v && is_assignable_v) - constexpr expected& operator=(unexpected&& e); - - // Rebinding assignment for reference E from reference G — binds unex_ to the external - // referent. - template - requires(is_reference_v && is_reference_v && is_constructible_v && - !reference_constructs_from_temporary_v) - constexpr expected& operator=(const unexpected& e); - - template - requires(is_reference_v && is_reference_v && is_constructible_v && - !reference_constructs_from_temporary_v) - constexpr expected& operator=(unexpected&& e); - - // Deleted for reference E with value G: would rebind E& to unexpected's temporary - // storage. - template - requires(is_reference_v && !is_reference_v) - constexpr expected& operator=(const unexpected&) = BEMAN_EXPECTED_DELETE_MSG( - "expected: cannot assign from unexpected; the value would dangle — " - "use unexpected"); - - template - requires(is_reference_v && !is_reference_v) - constexpr expected& operator=(unexpected&&) = BEMAN_EXPECTED_DELETE_MSG( - "expected: cannot assign from unexpected; the value would dangle — " - "use unexpected"); - - // emplace — rebind the reference - template - requires(is_constructible_v && !reference_constructs_from_temporary_v) - constexpr T& emplace(U&& u) noexcept(is_nothrow_constructible_v); - - // ------------------------------------------------------------------------- - // Swap - // ------------------------------------------------------------------------- - - constexpr void swap(expected& rhs) noexcept(is_nothrow_move_constructible_v && - (is_reference_v || - is_nothrow_swappable_v)) - requires((is_reference_v || is_swappable_v) && is_move_constructible_v); - - friend constexpr void swap(expected& x, expected& y) noexcept(noexcept(x.swap(y))) - requires((is_reference_v || is_swappable_v) && is_move_constructible_v); - - // ------------------------------------------------------------------------- - // 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() &&; - - // error() — shallow const: always returns E& regardless of const on expected - constexpr const E& error() const& noexcept; - constexpr E& error() & noexcept; - constexpr const E&& error() const&& noexcept; - constexpr E&& error() && noexcept; - - template> - requires(is_object_v && !is_array_v) - constexpr remove_cv_t value_or(U&& def) const; - - // Constraints spell error_value_type as its underlying trait expression rather than - // the member typedef: clang (through 22) fails to match an out-of-line constrained - // member of a partial specialization when the requires-clause names a member typedef - // of the class. - template - requires(is_copy_constructible_v>> && - is_convertible_v>>) - constexpr @\exposidnc{error-value-type}@ error_or(G&& def) const&; - - template - requires(is_move_constructible_v>> && - is_convertible_v>>) - constexpr @\exposidnc{error-value-type}@ error_or(G&& def) &&; - - // ------------------------------------------------------------------------- - // Monadic operations - // ------------------------------------------------------------------------- - - template - requires is_constructible_v - constexpr auto and_then(F&& f) &; - template - requires is_constructible_v - constexpr auto and_then(F&& f) &&; - template - requires is_constructible_v - constexpr auto and_then(F&& f) const&; - template - requires is_constructible_v - 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&&; - - // transform: f receives T& (value); error propagates as E; result is expected - template - requires is_constructible_v - constexpr auto transform(F&& f) &; - template - requires is_constructible_v - constexpr auto transform(F&& f) &&; - template - requires is_constructible_v - constexpr auto transform(F&& f) const&; - template - requires is_constructible_v - constexpr auto transform(F&& f) const&&; - - // transform_error: f receives E; value propagates as T&; result is expected - 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&&; - - // ------------------------------------------------------------------------- - // Equality operators (hidden friends) - // ------------------------------------------------------------------------- - - template - requires(!is_void_v) - friend constexpr bool operator==(const expected& x, const expected& y); - - template - requires(!@\exposidnc{is-expected-specialization}@::value) - friend constexpr bool operator==(const expected& x, const T2& val); - - template - friend constexpr bool operator==(const expected& x, const unexpected& e); - -private: - bool @\exposidnc{has-val}@; // exposition only - union { - T* @\exposidnc{val}@; // exposition only - unexpected @\exposidnc{unex}@; // exposition only +namespace std { + template + class @\libglobal{expected}@ { + private: + using @\exposidnc{error-value-type}@ = remove_cv_t>; // exposition only + + public: + using @\libmember{value_type}{expected}@ = T&; + using @\libmember{error_type}{expected}@ = E; + using @\libmember{unexpected_type}{expected}@ = unexpected; + + template using rebind = expected; + + // ------------------------------------------------------------------------- + // Constructors + // ------------------------------------------------------------------------- + + expected() = BEMAN_EXPECTED_DELETE_MSG( + "expected: no default constructor; T& cannot be null"); + + // Copy constructor (trivial path). Unconstrained; see the primary + // template's copy constructor for why. + constexpr expected(const expected&) = default; + + // Copy constructor (non-trivial path) + constexpr expected(const expected& rhs) noexcept(is_nothrow_copy_constructible_v) + requires(is_copy_constructible_v && !is_trivially_copy_constructible_v); + + // Move constructor (trivial path). Unconstrained; no explicit noexcept. + constexpr expected(expected&&) = default; + + // Move constructor (non-trivial path) + constexpr expected(expected&& rhs) noexcept(is_nothrow_move_constructible_v) + requires(is_move_constructible_v && !is_trivially_move_constructible_v); + + // Deleted: no in-place value constructor — T& cannot be constructed in-place + template + constexpr expected(in_place_t, Args&&...) = BEMAN_EXPECTED_DELETE_MSG( + "expected: no in-place value constructor; T& cannot be constructed " + "in-place — pass a U convertible to T&"); + + // Value constructor — takes U that can bind to T& + template + requires(!is_same_v, in_place_t> && + !is_same_v, expected> && + !@\exposidnc{is-unexpected-specialization}@>::value && + is_constructible_v && !reference_constructs_from_temporary_v) + constexpr explicit(!is_convertible_v) + expected(U&& u) noexcept(is_nothrow_constructible_v); + + // Deleted: binding a temporary to T& creates a dangling reference + template + requires(reference_constructs_from_temporary_v) + constexpr expected(U&&) = + BEMAN_EXPECTED_DELETE_MSG("expected: argument would bind a temporary that " + "dangles; pass an lvalue reference"); + + // Converting constructor from expected (copy) — value-E path + template + requires(!is_reference_v && is_constructible_v && + is_constructible_v && + !reference_constructs_from_temporary_v) + constexpr explicit(!is_convertible_v || !is_convertible_v) + expected(const expected& rhs); + + // Converting constructor from expected (move) — value-E path + template + requires(!is_reference_v && is_constructible_v && + is_constructible_v && !reference_constructs_from_temporary_v) + constexpr explicit(!is_convertible_v || !is_convertible_v) + expected(expected&& rhs); + + // Converting constructor from expected (copy/move) — reference-E path: only + // accepts sources whose error type G is itself a reference convertible to E. + template + requires(is_reference_v && is_reference_v && is_constructible_v && + is_convertible_v && !reference_constructs_from_temporary_v) + constexpr explicit(!is_convertible_v || !is_convertible_v) + expected(const expected& rhs); + + template + requires(is_reference_v && is_reference_v && is_constructible_v && + is_convertible_v && !reference_constructs_from_temporary_v) + constexpr explicit(!is_convertible_v || !is_convertible_v) + expected(expected&& rhs); + + // Constructor from unexpected const& / && — value-E path + template + requires(!is_reference_v && is_constructible_v) + constexpr explicit(!is_convertible_v) expected(const unexpected& e); + + template + requires(!is_reference_v && is_constructible_v) + constexpr explicit(!is_convertible_v) expected(unexpected&& e); + + // Constructor from unexpected — reference-E path. Allowed only when G is itself a + // reference, i.e. the source unexpected holds a reference to an external object, so + // binding E& to e.error() cannot dangle regardless of the source's value category. No + // const_cast is needed. + template + requires(is_reference_v && is_reference_v && is_constructible_v && + !reference_constructs_from_temporary_v) + constexpr explicit(!is_convertible_v) expected(const unexpected& e) noexcept; + + template + requires(is_reference_v && is_reference_v && is_constructible_v && + !reference_constructs_from_temporary_v) + constexpr explicit(!is_convertible_v) expected(unexpected&& e) noexcept; + + // Deleted for reference E with value G: the referent lives inside the unexpected + // object, so binding E& to it would dangle once a temporary source is destroyed. Use + // (unexpect, lvalue), or an unexpected holding an external object, instead. + template + requires(is_reference_v && !is_reference_v) + constexpr expected(const unexpected&) = BEMAN_EXPECTED_DELETE_MSG( + "expected: cannot construct from unexpected; the value would " + "dangle — use unexpected"); + + template + requires(is_reference_v && !is_reference_v) + constexpr expected(unexpected&&) = BEMAN_EXPECTED_DELETE_MSG( + "expected: cannot construct from unexpected; the value would " + "dangle — use unexpected"); + + // In-place constructor for error + template + requires(is_constructible_v && !@\exposidnc{unexpect-dangles-v}@) + constexpr explicit expected(unexpect_t, Args&&... args); + + // Deleted: single argument would bind E& to a temporary — dangling prevention + template + requires(@\exposidnc{unexpect-dangles-v}@) + constexpr expected(unexpect_t, Args&&...) = + BEMAN_EXPECTED_DELETE_MSG("expected: unexpect argument would bind a " + "temporary that dangles; pass an lvalue reference"); + + // Deleted catch-all: reference E, argument neither constructible nor a dangling case + template + requires(is_reference_v && !is_constructible_v && + !@\exposidnc{unexpect-dangles-v}@) + constexpr expected(unexpect_t, Args&&...) = BEMAN_EXPECTED_DELETE_MSG( + "expected: no viable conversion from the given argument(s) to E&"); + + // In-place constructor for error with initializer_list + template + requires(!is_reference_v && is_constructible_v&, Args...>) + constexpr explicit expected(unexpect_t, initializer_list il, Args&&... args); + + template + requires is_reference_v + constexpr expected(unexpect_t, initializer_list, Args&&...) = + BEMAN_EXPECTED_DELETE_MSG("expected: initializer-list error construction " + "cannot bind a reference; pass an lvalue reference"); + + // ------------------------------------------------------------------------- + // Destructor + // ------------------------------------------------------------------------- + + constexpr ~expected() + requires is_trivially_destructible_v + = default; + + constexpr ~expected() + requires(!is_trivially_destructible_v); + + // ------------------------------------------------------------------------- + // Assignment (rebind semantics) + // ------------------------------------------------------------------------- + + // Copy assignment (trivial path) + constexpr expected& operator=(const expected&) + requires(is_trivially_copy_constructible_v && + is_trivially_copy_assignable_v && is_trivially_destructible_v) + = default; + + // Copy assignment (non-trivial path) + constexpr expected& operator=(const expected& rhs) noexcept( + is_nothrow_copy_constructible_v && is_nothrow_copy_assignable_v) + requires((is_reference_v || + (is_copy_constructible_v && is_copy_assignable_v)) && + !(is_trivially_copy_constructible_v && + is_trivially_copy_assignable_v && is_trivially_destructible_v)); + + // Move assignment (trivial path) + constexpr expected& operator=(expected&&) noexcept + requires(is_trivially_move_constructible_v && + is_trivially_move_assignable_v && is_trivially_destructible_v) + = default; + + // Move assignment (non-trivial path) + constexpr expected& operator=(expected&& rhs) noexcept( + is_nothrow_move_constructible_v && is_nothrow_move_assignable_v) + requires((is_reference_v || + (is_move_constructible_v && is_move_assignable_v)) && + !(is_trivially_move_constructible_v && + is_trivially_move_assignable_v && is_trivially_destructible_v)); + + // Rebind reference from lvalue + template + requires(!is_same_v, expected> && + !@\exposidnc{is-unexpected-specialization}@>::value && + is_constructible_v && !reference_constructs_from_temporary_v) + constexpr expected& operator=(U&& u); + + // Assignment from unexpected — value-E path + template + requires(!is_reference_v && is_constructible_v && + is_assignable_v) + constexpr expected& operator=(const unexpected& e); + + template + requires(!is_reference_v && is_constructible_v && is_assignable_v) + constexpr expected& operator=(unexpected&& e); + + // Rebinding assignment for reference E from reference G — binds unex_ to the external + // referent. + template + requires(is_reference_v && is_reference_v && is_constructible_v && + !reference_constructs_from_temporary_v) + constexpr expected& operator=(const unexpected& e); + + template + requires(is_reference_v && is_reference_v && is_constructible_v && + !reference_constructs_from_temporary_v) + constexpr expected& operator=(unexpected&& e); + + // Deleted for reference E with value G: would rebind E& to unexpected's temporary + // storage. + template + requires(is_reference_v && !is_reference_v) + constexpr expected& operator=(const unexpected&) = BEMAN_EXPECTED_DELETE_MSG( + "expected: cannot assign from unexpected; the value would dangle — " + "use unexpected"); + + template + requires(is_reference_v && !is_reference_v) + constexpr expected& operator=(unexpected&&) = BEMAN_EXPECTED_DELETE_MSG( + "expected: cannot assign from unexpected; the value would dangle — " + "use unexpected"); + + // emplace — rebind the reference + template + requires(is_constructible_v && !reference_constructs_from_temporary_v) + constexpr T& emplace(U&& u) noexcept(is_nothrow_constructible_v); + + // ------------------------------------------------------------------------- + // Swap + // ------------------------------------------------------------------------- + + constexpr void swap(expected& rhs) noexcept(is_nothrow_move_constructible_v && + (is_reference_v || + is_nothrow_swappable_v)) + requires((is_reference_v || is_swappable_v) && is_move_constructible_v); + + friend constexpr void swap(expected& x, expected& y) noexcept(noexcept(x.swap(y))) + requires((is_reference_v || is_swappable_v) && is_move_constructible_v); + + // ------------------------------------------------------------------------- + // 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() &&; + + // error() — shallow const: always returns E& regardless of const on expected + constexpr const E& error() const& noexcept; + constexpr E& error() & noexcept; + constexpr const E&& error() const&& noexcept; + constexpr E&& error() && noexcept; + + template> + requires(is_object_v && !is_array_v) + constexpr remove_cv_t value_or(U&& def) const; + + // Constraints spell error_value_type as its underlying trait expression rather than + // the member typedef: clang (through 22) fails to match an out-of-line constrained + // member of a partial specialization when the requires-clause names a member typedef + // of the class. + template + requires(is_copy_constructible_v>> && + is_convertible_v>>) + constexpr @\exposidnc{error-value-type}@ error_or(G&& def) const&; + + template + requires(is_move_constructible_v>> && + is_convertible_v>>) + constexpr @\exposidnc{error-value-type}@ error_or(G&& def) &&; + + // ------------------------------------------------------------------------- + // Monadic operations + // ------------------------------------------------------------------------- + + template + requires is_constructible_v + constexpr auto and_then(F&& f) &; + template + requires is_constructible_v + constexpr auto and_then(F&& f) &&; + template + requires is_constructible_v + constexpr auto and_then(F&& f) const&; + template + requires is_constructible_v + 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&&; + + // transform: f receives T& (value); error propagates as E; result is expected + template + requires is_constructible_v + constexpr auto transform(F&& f) &; + template + requires is_constructible_v + constexpr auto transform(F&& f) &&; + template + requires is_constructible_v + constexpr auto transform(F&& f) const&; + template + requires is_constructible_v + constexpr auto transform(F&& f) const&&; + + // transform_error: f receives E; value propagates as T&; result is expected + 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&&; + + // ------------------------------------------------------------------------- + // Equality operators (hidden friends) + // ------------------------------------------------------------------------- + + template + requires(!is_void_v) + friend constexpr bool operator==(const expected& x, const expected& y); + + template + requires(!@\exposidnc{is-expected-specialization}@::value) + friend constexpr bool operator==(const expected& x, const T2& val); + + template + friend constexpr bool operator==(const expected& x, const unexpected& e); + + private: + bool @\exposidnc{has-val}@; // exposition only + union { + T* @\exposidnc{val}@; // exposition only + unexpected @\exposidnc{unex}@; // exposition only + }; }; -}; +} \end{codeblock} \begin{itemdescr} diff --git a/papers/wording/fragments/expected.bad.tex b/papers/wording/fragments/expected.bad.tex index 5c47ae2..a88375d 100644 --- a/papers/wording/fragments/expected.bad.tex +++ b/papers/wording/fragments/expected.bad.tex @@ -12,19 +12,21 @@ \end{itemdescr} \begin{codeblock} -template -class @\libglobal{bad_expected_access}@ : public bad_expected_access { -public: - BEMAN_EXPECTED_CONSTEXPR_EXCEPTION explicit bad_expected_access(E e); - BEMAN_EXPECTED_CONSTEXPR_EXCEPTION 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}@; // exposition only -}; +namespace std { + template + class @\libglobal{bad_expected_access}@ : public bad_expected_access { + public: + BEMAN_EXPECTED_CONSTEXPR_EXCEPTION explicit bad_expected_access(E e); + BEMAN_EXPECTED_CONSTEXPR_EXCEPTION 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}@; // exposition only + }; +} \end{codeblock} \begin{itemdescr} diff --git a/papers/wording/fragments/expected.bad.void.tex b/papers/wording/fragments/expected.bad.void.tex index 55b9c7d..7498f4e 100644 --- a/papers/wording/fragments/expected.bad.void.tex +++ b/papers/wording/fragments/expected.bad.void.tex @@ -1,21 +1,23 @@ \rSec2[expected.bad.void]{Class template specialization bad_expected_access} \begin{codeblock} -template<> -class @\libglobal{bad_expected_access}@ : public exception { -protected: - BEMAN_EXPECTED_CONSTEXPR_EXCEPTION bad_expected_access() noexcept = default; - BEMAN_EXPECTED_CONSTEXPR_EXCEPTION bad_expected_access( - const bad_expected_access&) noexcept = default; - BEMAN_EXPECTED_CONSTEXPR_EXCEPTION bad_expected_access( - bad_expected_access&&) noexcept = default; - BEMAN_EXPECTED_CONSTEXPR_EXCEPTION bad_expected_access& operator=( - const bad_expected_access&) noexcept = default; - BEMAN_EXPECTED_CONSTEXPR_EXCEPTION bad_expected_access& operator=( - bad_expected_access&&) noexcept = default; - BEMAN_EXPECTED_CONSTEXPR_EXCEPTION ~bad_expected_access() = default; +namespace std { + template<> + class @\libglobal{bad_expected_access}@ : public exception { + protected: + BEMAN_EXPECTED_CONSTEXPR_EXCEPTION bad_expected_access() noexcept = default; + BEMAN_EXPECTED_CONSTEXPR_EXCEPTION bad_expected_access( + const bad_expected_access&) noexcept = default; + BEMAN_EXPECTED_CONSTEXPR_EXCEPTION bad_expected_access( + bad_expected_access&&) noexcept = default; + BEMAN_EXPECTED_CONSTEXPR_EXCEPTION bad_expected_access& operator=( + const bad_expected_access&) noexcept = default; + BEMAN_EXPECTED_CONSTEXPR_EXCEPTION bad_expected_access& operator=( + bad_expected_access&&) noexcept = default; + BEMAN_EXPECTED_CONSTEXPR_EXCEPTION ~bad_expected_access() = default; -public: - BEMAN_EXPECTED_CONSTEXPR_EXCEPTION const char* what() const noexcept override; -}; + public: + BEMAN_EXPECTED_CONSTEXPR_EXCEPTION const char* what() const noexcept override; + }; +} \end{codeblock} diff --git a/papers/wording/fragments/expected.expected.tex b/papers/wording/fragments/expected.expected.tex index 0bfa8f6..b106da0 100644 --- a/papers/wording/fragments/expected.expected.tex +++ b/papers/wording/fragments/expected.expected.tex @@ -3,436 +3,438 @@ \rSec3[expected.object.general]{General} \begin{codeblock} -template -class @\libglobal{expected}@ { -private: - using @\exposidnc{error-value-type}@ = remove_cv_t>; // exposition only - -public: - using @\libmember{value_type}{expected}@ = T; - using @\libmember{error_type}{expected}@ = E; - using @\libmember{unexpected_type}{expected}@ = unexpected; - - template using rebind = expected; - - // ------------------------------------------------------------------------- - // [expected.object.cons] Constructors - // ------------------------------------------------------------------------- - - // Default constructor: value-initializes T - constexpr expected() noexcept(is_nothrow_default_constructible_v) - requires is_default_constructible_v; - - // Copy constructor (trivial path). Unconstrained on purpose: this is the - // sole declaration when T or E is not copy constructible at all (it is - // then implicitly defined as deleted), and the more-constrained - // non-trivial-path overload below is selected over it by constraint - // subsumption whenever it is viable. - constexpr expected(const expected&) = default; - - // Copy constructor (non-trivial path) - constexpr expected(const expected& rhs) noexcept(is_nothrow_copy_constructible_v && - is_nothrow_copy_constructible_v) - requires(is_copy_constructible_v && is_copy_constructible_v && - !(is_trivially_copy_constructible_v && - is_trivially_copy_constructible_v)); - - // Move constructor (trivial path). Unconstrained; see the copy - // constructor above for why. No explicit noexcept: let the compiler - // deduce it, so a non-movable-at-all E deletes rather than mismatches. - constexpr expected(expected&&) = default; - - // Move constructor (non-trivial path) - constexpr expected(expected&& rhs) noexcept(is_nothrow_move_constructible_v && - is_nothrow_move_constructible_v) - requires(is_move_constructible_v && is_move_constructible_v && - !(is_trivially_move_constructible_v && - is_trivially_move_constructible_v)); - - // Converting copy constructor from expected — value-E path - template - requires(!is_reference_v && is_constructible_v && - is_constructible_v && - (is_same_v> || - !@\exposidnc{converts-from-any-cvref}@>) && - !is_constructible_v, expected&> && - !is_constructible_v, expected&&> && - !is_constructible_v, const expected&> && - !is_constructible_v, const expected&&>) - constexpr explicit(!is_convertible_v || !is_convertible_v) - expected(const expected& rhs); - - // Converting move constructor from expected — value-E path - template - requires(!is_reference_v && is_constructible_v && - is_constructible_v && - (is_same_v> || - !@\exposidnc{converts-from-any-cvref}@>) && - !is_constructible_v, expected&> && - !is_constructible_v, expected&&> && - !is_constructible_v, const expected&> && - !is_constructible_v, const expected&&>) - constexpr explicit(!is_convertible_v || !is_convertible_v) - expected(expected&& rhs); - - // Converting constructor from expected — reference-E path: only accepts sources - // whose error type G is itself a reference convertible to E. - template - requires(is_reference_v && is_reference_v && - is_constructible_v && is_convertible_v) - constexpr explicit(!is_convertible_v || !is_convertible_v) - expected(const expected& rhs); - - template - requires(is_reference_v && is_reference_v && is_constructible_v && - is_convertible_v) - constexpr explicit(!is_convertible_v || !is_convertible_v) - expected(expected&& rhs); - - // Constructor from value U&& - template> - requires(!is_same_v, in_place_t> && - !is_same_v, unexpect_t> && - !is_same_v, expected> && is_constructible_v && - !@\exposidnc{is-unexpected-specialization}@>::value && - (!is_same_v> || - !@\exposidnc{is-expected-specialization}@>::value)) - constexpr explicit(!is_convertible_v) expected(U&& v); - - // Constructor from unexpected const& / && — value-E path - template - requires(!is_reference_v && is_constructible_v) - constexpr explicit(!is_convertible_v) expected(const unexpected& e); - - template - requires(!is_reference_v && is_constructible_v) - constexpr explicit(!is_convertible_v) expected(unexpected&& e); - - // Constructor from unexpected — reference-E path. Allowed only when G is itself a - // reference, i.e. the source unexpected holds a reference to an external object, so - // binding E& to e.error() cannot dangle regardless of the source's value category. No - // const_cast is needed. - template - requires(is_reference_v && is_reference_v && is_constructible_v && - !reference_constructs_from_temporary_v) - constexpr explicit(!is_convertible_v) expected(const unexpected& e) noexcept; - - template - requires(is_reference_v && is_reference_v && is_constructible_v && - !reference_constructs_from_temporary_v) - constexpr explicit(!is_convertible_v) expected(unexpected&& e) noexcept; - - // Deleted for reference E with value G: the referent lives inside the unexpected - // object, so binding E& to it would dangle once a temporary source is destroyed. Use - // (unexpect, lvalue), or an unexpected holding an external object, instead. - template - requires(is_reference_v && !is_reference_v) - constexpr expected(const unexpected&) = BEMAN_EXPECTED_DELETE_MSG( - "expected: cannot construct from unexpected; the value would dangle " - "— use unexpected"); - - template - requires(is_reference_v && !is_reference_v) - constexpr expected(unexpected&&) = BEMAN_EXPECTED_DELETE_MSG( - "expected: cannot construct from unexpected; the value would dangle " - "— use unexpected"); - - // In-place constructor for value - template - requires is_constructible_v - constexpr explicit expected(in_place_t, Args&&... args); - - // In-place constructor for value with initializer_list - template - requires is_constructible_v&, Args...> - constexpr explicit expected(in_place_t, initializer_list il, Args&&... args); - - // In-place constructor for error - template - requires(is_constructible_v && !@\exposidnc{unexpect-dangles-v}@) - constexpr explicit expected(unexpect_t, Args&&... args); - - // Deleted: single argument would bind E& to a temporary — dangling prevention - template - requires(@\exposidnc{unexpect-dangles-v}@) - constexpr expected(unexpect_t, Args&&...) = - BEMAN_EXPECTED_DELETE_MSG("expected: unexpect argument would bind a " - "temporary that dangles; pass an lvalue reference"); - - // Deleted catch-all: reference E, argument neither constructible nor a dangling case - // (e.g. binding a non-const E& from a const lvalue). - template - requires(is_reference_v && !is_constructible_v && - !@\exposidnc{unexpect-dangles-v}@) - constexpr expected(unexpect_t, Args&&...) = BEMAN_EXPECTED_DELETE_MSG( - "expected: no viable conversion from the given argument(s) to E&"); - - // In-place constructor for error with initializer_list - template - requires(!is_reference_v && is_constructible_v&, Args...>) - constexpr explicit expected(unexpect_t, initializer_list il, Args&&... args); - - template - requires is_reference_v - constexpr expected(unexpect_t, initializer_list, Args&&...) = - BEMAN_EXPECTED_DELETE_MSG("expected: initializer-list error construction " - "cannot bind a reference; pass an lvalue reference"); - - // ------------------------------------------------------------------------- - // [expected.object.dtor] Destructor - // ------------------------------------------------------------------------- - - constexpr ~expected() - requires(is_trivially_destructible_v && is_trivially_destructible_v) - = default; - - constexpr ~expected() - requires(!(is_trivially_destructible_v && is_trivially_destructible_v)); - - // ------------------------------------------------------------------------- - // [expected.object.assign] Assignment - // ------------------------------------------------------------------------- - - // Copy assignment (trivial path) - constexpr expected& operator=(const expected&) - requires(is_trivially_copy_constructible_v && - is_trivially_copy_assignable_v && is_trivially_destructible_v && - is_trivially_copy_constructible_v && - is_trivially_copy_assignable_v && is_trivially_destructible_v) - = default; - - // Copy assignment (non-trivial path) - constexpr expected& operator=(const expected& rhs) noexcept( - is_nothrow_copy_constructible_v && is_nothrow_copy_assignable_v && - is_nothrow_copy_constructible_v && is_nothrow_copy_assignable_v) - requires(is_copy_constructible_v && is_copy_assignable_v && - (is_reference_v || - (is_copy_constructible_v && is_copy_assignable_v)) && - (is_nothrow_move_constructible_v || - is_nothrow_move_constructible_v) && - !(is_trivially_copy_constructible_v && +namespace std { + template + class @\libglobal{expected}@ { + private: + using @\exposidnc{error-value-type}@ = remove_cv_t>; // exposition only + + public: + using @\libmember{value_type}{expected}@ = T; + using @\libmember{error_type}{expected}@ = E; + using @\libmember{unexpected_type}{expected}@ = unexpected; + + template using rebind = expected; + + // ------------------------------------------------------------------------- + // [expected.object.cons] Constructors + // ------------------------------------------------------------------------- + + // Default constructor: value-initializes T + constexpr expected() noexcept(is_nothrow_default_constructible_v) + requires is_default_constructible_v; + + // Copy constructor (trivial path). Unconstrained on purpose: this is the + // sole declaration when T or E is not copy constructible at all (it is + // then implicitly defined as deleted), and the more-constrained + // non-trivial-path overload below is selected over it by constraint + // subsumption whenever it is viable. + constexpr expected(const expected&) = default; + + // Copy constructor (non-trivial path) + constexpr expected(const expected& rhs) noexcept(is_nothrow_copy_constructible_v && + is_nothrow_copy_constructible_v) + requires(is_copy_constructible_v && is_copy_constructible_v && + !(is_trivially_copy_constructible_v && + is_trivially_copy_constructible_v)); + + // Move constructor (trivial path). Unconstrained; see the copy + // constructor above for why. No explicit noexcept: let the compiler + // deduce it, so a non-movable-at-all E deletes rather than mismatches. + constexpr expected(expected&&) = default; + + // Move constructor (non-trivial path) + constexpr expected(expected&& rhs) noexcept(is_nothrow_move_constructible_v && + is_nothrow_move_constructible_v) + requires(is_move_constructible_v && is_move_constructible_v && + !(is_trivially_move_constructible_v && + is_trivially_move_constructible_v)); + + // Converting copy constructor from expected — value-E path + template + requires(!is_reference_v && is_constructible_v && + is_constructible_v && + (is_same_v> || + !@\exposidnc{converts-from-any-cvref}@>) && + !is_constructible_v, expected&> && + !is_constructible_v, expected&&> && + !is_constructible_v, const expected&> && + !is_constructible_v, const expected&&>) + constexpr explicit(!is_convertible_v || !is_convertible_v) + expected(const expected& rhs); + + // Converting move constructor from expected — value-E path + template + requires(!is_reference_v && is_constructible_v && + is_constructible_v && + (is_same_v> || + !@\exposidnc{converts-from-any-cvref}@>) && + !is_constructible_v, expected&> && + !is_constructible_v, expected&&> && + !is_constructible_v, const expected&> && + !is_constructible_v, const expected&&>) + constexpr explicit(!is_convertible_v || !is_convertible_v) + expected(expected&& rhs); + + // Converting constructor from expected — reference-E path: only accepts sources + // whose error type G is itself a reference convertible to E. + template + requires(is_reference_v && is_reference_v && + is_constructible_v && is_convertible_v) + constexpr explicit(!is_convertible_v || !is_convertible_v) + expected(const expected& rhs); + + template + requires(is_reference_v && is_reference_v && is_constructible_v && + is_convertible_v) + constexpr explicit(!is_convertible_v || !is_convertible_v) + expected(expected&& rhs); + + // Constructor from value U&& + template> + requires(!is_same_v, in_place_t> && + !is_same_v, unexpect_t> && + !is_same_v, expected> && is_constructible_v && + !@\exposidnc{is-unexpected-specialization}@>::value && + (!is_same_v> || + !@\exposidnc{is-expected-specialization}@>::value)) + constexpr explicit(!is_convertible_v) expected(U&& v); + + // Constructor from unexpected const& / && — value-E path + template + requires(!is_reference_v && is_constructible_v) + constexpr explicit(!is_convertible_v) expected(const unexpected& e); + + template + requires(!is_reference_v && is_constructible_v) + constexpr explicit(!is_convertible_v) expected(unexpected&& e); + + // Constructor from unexpected — reference-E path. Allowed only when G is itself a + // reference, i.e. the source unexpected holds a reference to an external object, so + // binding E& to e.error() cannot dangle regardless of the source's value category. No + // const_cast is needed. + template + requires(is_reference_v && is_reference_v && is_constructible_v && + !reference_constructs_from_temporary_v) + constexpr explicit(!is_convertible_v) expected(const unexpected& e) noexcept; + + template + requires(is_reference_v && is_reference_v && is_constructible_v && + !reference_constructs_from_temporary_v) + constexpr explicit(!is_convertible_v) expected(unexpected&& e) noexcept; + + // Deleted for reference E with value G: the referent lives inside the unexpected + // object, so binding E& to it would dangle once a temporary source is destroyed. Use + // (unexpect, lvalue), or an unexpected holding an external object, instead. + template + requires(is_reference_v && !is_reference_v) + constexpr expected(const unexpected&) = BEMAN_EXPECTED_DELETE_MSG( + "expected: cannot construct from unexpected; the value would dangle " + "— use unexpected"); + + template + requires(is_reference_v && !is_reference_v) + constexpr expected(unexpected&&) = BEMAN_EXPECTED_DELETE_MSG( + "expected: cannot construct from unexpected; the value would dangle " + "— use unexpected"); + + // In-place constructor for value + template + requires is_constructible_v + constexpr explicit expected(in_place_t, Args&&... args); + + // In-place constructor for value with initializer_list + template + requires is_constructible_v&, Args...> + constexpr explicit expected(in_place_t, initializer_list il, Args&&... args); + + // In-place constructor for error + template + requires(is_constructible_v && !@\exposidnc{unexpect-dangles-v}@) + constexpr explicit expected(unexpect_t, Args&&... args); + + // Deleted: single argument would bind E& to a temporary — dangling prevention + template + requires(@\exposidnc{unexpect-dangles-v}@) + constexpr expected(unexpect_t, Args&&...) = + BEMAN_EXPECTED_DELETE_MSG("expected: unexpect argument would bind a " + "temporary that dangles; pass an lvalue reference"); + + // Deleted catch-all: reference E, argument neither constructible nor a dangling case + // (e.g. binding a non-const E& from a const lvalue). + template + requires(is_reference_v && !is_constructible_v && + !@\exposidnc{unexpect-dangles-v}@) + constexpr expected(unexpect_t, Args&&...) = BEMAN_EXPECTED_DELETE_MSG( + "expected: no viable conversion from the given argument(s) to E&"); + + // In-place constructor for error with initializer_list + template + requires(!is_reference_v && is_constructible_v&, Args...>) + constexpr explicit expected(unexpect_t, initializer_list il, Args&&... args); + + template + requires is_reference_v + constexpr expected(unexpect_t, initializer_list, Args&&...) = + BEMAN_EXPECTED_DELETE_MSG("expected: initializer-list error construction " + "cannot bind a reference; pass an lvalue reference"); + + // ------------------------------------------------------------------------- + // [expected.object.dtor] Destructor + // ------------------------------------------------------------------------- + + constexpr ~expected() + requires(is_trivially_destructible_v && is_trivially_destructible_v) + = default; + + constexpr ~expected() + requires(!(is_trivially_destructible_v && is_trivially_destructible_v)); + + // ------------------------------------------------------------------------- + // [expected.object.assign] Assignment + // ------------------------------------------------------------------------- + + // Copy assignment (trivial path) + constexpr expected& operator=(const expected&) + requires(is_trivially_copy_constructible_v && is_trivially_copy_assignable_v && is_trivially_destructible_v && is_trivially_copy_constructible_v && - is_trivially_copy_assignable_v && is_trivially_destructible_v)); - - // Move assignment (trivial path) - constexpr expected& operator=(expected&&) noexcept - requires(is_trivially_move_constructible_v && - is_trivially_move_assignable_v && is_trivially_destructible_v && - is_trivially_move_constructible_v && - is_trivially_move_assignable_v && is_trivially_destructible_v) - = default; - - // Move assignment (non-trivial path) - constexpr expected& operator=(expected&& rhs) noexcept( - is_nothrow_move_constructible_v && is_nothrow_move_assignable_v && - is_nothrow_move_constructible_v && is_nothrow_move_assignable_v) - requires(is_move_constructible_v && is_move_assignable_v && - (is_reference_v || - (is_move_constructible_v && is_move_assignable_v)) && - (is_nothrow_move_constructible_v || - is_nothrow_move_constructible_v) && - !(is_trivially_move_constructible_v && + is_trivially_copy_assignable_v && is_trivially_destructible_v) + = default; + + // Copy assignment (non-trivial path) + constexpr expected& operator=(const expected& rhs) noexcept( + is_nothrow_copy_constructible_v && is_nothrow_copy_assignable_v && + is_nothrow_copy_constructible_v && is_nothrow_copy_assignable_v) + requires(is_copy_constructible_v && is_copy_assignable_v && + (is_reference_v || + (is_copy_constructible_v && is_copy_assignable_v)) && + (is_nothrow_move_constructible_v || + is_nothrow_move_constructible_v) && + !(is_trivially_copy_constructible_v && + is_trivially_copy_assignable_v && is_trivially_destructible_v && + is_trivially_copy_constructible_v && + is_trivially_copy_assignable_v && is_trivially_destructible_v)); + + // Move assignment (trivial path) + constexpr expected& operator=(expected&&) noexcept + requires(is_trivially_move_constructible_v && is_trivially_move_assignable_v && is_trivially_destructible_v && is_trivially_move_constructible_v && - is_trivially_move_assignable_v && is_trivially_destructible_v)); - - // Assignment from value U&& - template> - requires(!is_same_v> && - !@\exposidnc{is-unexpected-specialization}@>::value && - is_constructible_v && is_assignable_v && - (is_nothrow_constructible_v || is_nothrow_move_constructible_v || - is_nothrow_move_constructible_v)) - constexpr expected& operator=(U&& v); - - // Assignment from unexpected — value-E path - template - requires(!is_reference_v && is_constructible_v && - is_assignable_v && - (is_nothrow_constructible_v || - is_nothrow_move_constructible_v || is_nothrow_move_constructible_v)) - constexpr expected& operator=(const unexpected& e); - - template - requires(!is_reference_v && is_constructible_v && is_assignable_v && - (is_nothrow_constructible_v || is_nothrow_move_constructible_v || - is_nothrow_move_constructible_v)) - constexpr expected& operator=(unexpected&& e); - - // Rebinding assignment for reference E from reference G — binds unex_ to the external - // referent (never dangles; pointer store is noexcept). Mirrors the reference-E - // constructor. - template - requires(is_reference_v && is_reference_v && is_constructible_v && - !reference_constructs_from_temporary_v) - constexpr expected& operator=(const unexpected& e); - - template - requires(is_reference_v && is_reference_v && is_constructible_v && - !reference_constructs_from_temporary_v) - constexpr expected& operator=(unexpected&& e); - - // Deleted for reference E with value G: would rebind E& to unexpected's temporary - // storage. - template - requires(is_reference_v && !is_reference_v) - constexpr expected& operator=(const unexpected&) = - BEMAN_EXPECTED_DELETE_MSG("expected: cannot assign from unexpected; " - "the value would dangle — use unexpected"); - - template - requires(is_reference_v && !is_reference_v) - constexpr expected& operator=(unexpected&&) = - BEMAN_EXPECTED_DELETE_MSG("expected: cannot assign from unexpected; " - "the value would dangle — use unexpected"); - - // Emplace: destroy current value/error, construct value in-place - template - requires is_nothrow_constructible_v - constexpr T& emplace(Args&&... args) noexcept; - - template - requires is_nothrow_constructible_v&, Args...> - constexpr T& emplace(initializer_list il, Args&&... args) noexcept; - - // ------------------------------------------------------------------------- - // [expected.object.swap] Swap - // ------------------------------------------------------------------------- - - constexpr void swap(expected& rhs) noexcept(is_nothrow_move_constructible_v && - is_nothrow_swappable_v && - is_nothrow_move_constructible_v && - (is_reference_v || - is_nothrow_swappable_v)) - requires(is_swappable_v && (is_reference_v || is_swappable_v) && - is_move_constructible_v && is_move_constructible_v && - (is_nothrow_move_constructible_v || is_nothrow_move_constructible_v)) - ; - - friend constexpr void swap(expected& x, expected& y) noexcept(noexcept(x.swap(y))) - requires(is_swappable_v && (is_reference_v || is_swappable_v) && - is_move_constructible_v && is_move_constructible_v && - (is_nothrow_move_constructible_v || is_nothrow_move_constructible_v)) - ; - - // ------------------------------------------------------------------------- - // [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&; - constexpr T& value() &; - constexpr const 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 T value_or(U&& def) const&; - - template> constexpr T value_or(U&& def) &&; - - template - requires(is_copy_constructible_v<@\exposidnc{error-value-type}@> && - is_convertible_v) - constexpr @\exposidnc{error-value-type}@ error_or(G&& def) const&; - - template - requires(is_move_constructible_v<@\exposidnc{error-value-type}@> && - is_convertible_v) - constexpr @\exposidnc{error-value-type}@ error_or(G&& def) &&; - - // ------------------------------------------------------------------------- - // [expected.object.monadic] Monadic operations - // ------------------------------------------------------------------------- - - template - requires is_constructible_v - constexpr auto and_then(F&& f) &; - template - requires is_constructible_v - constexpr auto and_then(F&& f) &&; - template - requires is_constructible_v - constexpr auto and_then(F&& f) const&; - template - requires is_constructible_v - constexpr auto and_then(F&& f) const&&; - - template - requires is_constructible_v - constexpr auto or_else(F&& f) &; - template - requires is_constructible_v - constexpr auto or_else(F&& f) &&; - template - requires is_constructible_v - constexpr auto or_else(F&& f) const&; - template - requires is_constructible_v - constexpr auto or_else(F&& f) const&&; - - template - requires is_constructible_v - constexpr auto transform(F&& f) &; - template - requires is_constructible_v - constexpr auto transform(F&& f) &&; - template - requires is_constructible_v - constexpr auto transform(F&& f) const&; - template - requires is_constructible_v - constexpr auto transform(F&& f) const&&; - - template - requires is_constructible_v - constexpr auto transform_error(F&& f) &; - template - requires is_constructible_v - constexpr auto transform_error(F&& f) &&; - template - requires is_constructible_v - constexpr auto transform_error(F&& f) const&; - template - requires is_constructible_v - constexpr auto transform_error(F&& f) const&&; - - // ------------------------------------------------------------------------- - // [expected.object.eq] Equality operators (hidden friends) - // ------------------------------------------------------------------------- - - template - requires(!is_void_v) - friend constexpr bool operator==(const expected& x, const expected& y); - - template - requires(!@\exposidnc{is-expected-specialization}@::value) - friend constexpr bool operator==(const expected& x, const T2& val); - - template - friend constexpr bool operator==(const expected& x, const unexpected& e); - -private: - bool @\exposidnc{has-val}@; // exposition only - union { - T @\exposidnc{val}@; // exposition only - unexpected @\exposidnc{unex}@; // exposition only + is_trivially_move_assignable_v && is_trivially_destructible_v) + = default; + + // Move assignment (non-trivial path) + constexpr expected& operator=(expected&& rhs) noexcept( + is_nothrow_move_constructible_v && is_nothrow_move_assignable_v && + is_nothrow_move_constructible_v && is_nothrow_move_assignable_v) + requires(is_move_constructible_v && is_move_assignable_v && + (is_reference_v || + (is_move_constructible_v && is_move_assignable_v)) && + (is_nothrow_move_constructible_v || + is_nothrow_move_constructible_v) && + !(is_trivially_move_constructible_v && + is_trivially_move_assignable_v && is_trivially_destructible_v && + is_trivially_move_constructible_v && + is_trivially_move_assignable_v && is_trivially_destructible_v)); + + // Assignment from value U&& + template> + requires(!is_same_v> && + !@\exposidnc{is-unexpected-specialization}@>::value && + is_constructible_v && is_assignable_v && + (is_nothrow_constructible_v || is_nothrow_move_constructible_v || + is_nothrow_move_constructible_v)) + constexpr expected& operator=(U&& v); + + // Assignment from unexpected — value-E path + template + requires(!is_reference_v && is_constructible_v && + is_assignable_v && + (is_nothrow_constructible_v || + is_nothrow_move_constructible_v || is_nothrow_move_constructible_v)) + constexpr expected& operator=(const unexpected& e); + + template + requires(!is_reference_v && is_constructible_v && is_assignable_v && + (is_nothrow_constructible_v || is_nothrow_move_constructible_v || + is_nothrow_move_constructible_v)) + constexpr expected& operator=(unexpected&& e); + + // Rebinding assignment for reference E from reference G — binds unex_ to the external + // referent (never dangles; pointer store is noexcept). Mirrors the reference-E + // constructor. + template + requires(is_reference_v && is_reference_v && is_constructible_v && + !reference_constructs_from_temporary_v) + constexpr expected& operator=(const unexpected& e); + + template + requires(is_reference_v && is_reference_v && is_constructible_v && + !reference_constructs_from_temporary_v) + constexpr expected& operator=(unexpected&& e); + + // Deleted for reference E with value G: would rebind E& to unexpected's temporary + // storage. + template + requires(is_reference_v && !is_reference_v) + constexpr expected& operator=(const unexpected&) = + BEMAN_EXPECTED_DELETE_MSG("expected: cannot assign from unexpected; " + "the value would dangle — use unexpected"); + + template + requires(is_reference_v && !is_reference_v) + constexpr expected& operator=(unexpected&&) = + BEMAN_EXPECTED_DELETE_MSG("expected: cannot assign from unexpected; " + "the value would dangle — use unexpected"); + + // Emplace: destroy current value/error, construct value in-place + template + requires is_nothrow_constructible_v + constexpr T& emplace(Args&&... args) noexcept; + + template + requires is_nothrow_constructible_v&, Args...> + constexpr T& emplace(initializer_list il, Args&&... args) noexcept; + + // ------------------------------------------------------------------------- + // [expected.object.swap] Swap + // ------------------------------------------------------------------------- + + constexpr void swap(expected& rhs) noexcept(is_nothrow_move_constructible_v && + is_nothrow_swappable_v && + is_nothrow_move_constructible_v && + (is_reference_v || + is_nothrow_swappable_v)) + requires(is_swappable_v && (is_reference_v || is_swappable_v) && + is_move_constructible_v && is_move_constructible_v && + (is_nothrow_move_constructible_v || is_nothrow_move_constructible_v)) + ; + + friend constexpr void swap(expected& x, expected& y) noexcept(noexcept(x.swap(y))) + requires(is_swappable_v && (is_reference_v || is_swappable_v) && + is_move_constructible_v && is_move_constructible_v && + (is_nothrow_move_constructible_v || is_nothrow_move_constructible_v)) + ; + + // ------------------------------------------------------------------------- + // [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&; + constexpr T& value() &; + constexpr const 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 T value_or(U&& def) const&; + + template> constexpr T value_or(U&& def) &&; + + template + requires(is_copy_constructible_v<@\exposidnc{error-value-type}@> && + is_convertible_v) + constexpr @\exposidnc{error-value-type}@ error_or(G&& def) const&; + + template + requires(is_move_constructible_v<@\exposidnc{error-value-type}@> && + is_convertible_v) + constexpr @\exposidnc{error-value-type}@ error_or(G&& def) &&; + + // ------------------------------------------------------------------------- + // [expected.object.monadic] Monadic operations + // ------------------------------------------------------------------------- + + template + requires is_constructible_v + constexpr auto and_then(F&& f) &; + template + requires is_constructible_v + constexpr auto and_then(F&& f) &&; + template + requires is_constructible_v + constexpr auto and_then(F&& f) const&; + template + requires is_constructible_v + constexpr auto and_then(F&& f) const&&; + + template + requires is_constructible_v + constexpr auto or_else(F&& f) &; + template + requires is_constructible_v + constexpr auto or_else(F&& f) &&; + template + requires is_constructible_v + constexpr auto or_else(F&& f) const&; + template + requires is_constructible_v + constexpr auto or_else(F&& f) const&&; + + template + requires is_constructible_v + constexpr auto transform(F&& f) &; + template + requires is_constructible_v + constexpr auto transform(F&& f) &&; + template + requires is_constructible_v + constexpr auto transform(F&& f) const&; + template + requires is_constructible_v + constexpr auto transform(F&& f) const&&; + + template + requires is_constructible_v + constexpr auto transform_error(F&& f) &; + template + requires is_constructible_v + constexpr auto transform_error(F&& f) &&; + template + requires is_constructible_v + constexpr auto transform_error(F&& f) const&; + template + requires is_constructible_v + constexpr auto transform_error(F&& f) const&&; + + // ------------------------------------------------------------------------- + // [expected.object.eq] Equality operators (hidden friends) + // ------------------------------------------------------------------------- + + template + requires(!is_void_v) + friend constexpr bool operator==(const expected& x, const expected& y); + + template + requires(!@\exposidnc{is-expected-specialization}@::value) + friend constexpr bool operator==(const expected& x, const T2& val); + + template + friend constexpr bool operator==(const expected& x, const unexpected& e); + + private: + bool @\exposidnc{has-val}@; // exposition only + union { + T @\exposidnc{val}@; // exposition only + unexpected @\exposidnc{unex}@; // exposition only + }; }; -}; +} \end{codeblock} \begin{itemdescr} diff --git a/papers/wording/fragments/expected.ref.tex b/papers/wording/fragments/expected.ref.tex index 0a4399d..76ac474 100644 --- a/papers/wording/fragments/expected.ref.tex +++ b/papers/wording/fragments/expected.ref.tex @@ -3,363 +3,365 @@ \rSec3[expected.ref.general]{General} \begin{codeblock} -template -class @\libglobal{expected}@ { -private: - using @\exposidnc{error-value-type}@ = remove_cv_t>; // exposition only - -public: - using @\libmember{value_type}{expected}@ = T&; - using @\libmember{error_type}{expected}@ = E; - using @\libmember{unexpected_type}{expected}@ = unexpected; - - template using rebind = expected; - - // ------------------------------------------------------------------------- - // Constructors - // ------------------------------------------------------------------------- - - expected() = BEMAN_EXPECTED_DELETE_MSG( - "expected: no default constructor; T& cannot be null"); - - // Copy constructor (trivial path). Unconstrained; see the primary - // template's copy constructor for why. - constexpr expected(const expected&) = default; - - // Copy constructor (non-trivial path) - constexpr expected(const expected& rhs) noexcept(is_nothrow_copy_constructible_v) - requires(is_copy_constructible_v && !is_trivially_copy_constructible_v); - - // Move constructor (trivial path). Unconstrained; no explicit noexcept. - constexpr expected(expected&&) = default; - - // Move constructor (non-trivial path) - constexpr expected(expected&& rhs) noexcept(is_nothrow_move_constructible_v) - requires(is_move_constructible_v && !is_trivially_move_constructible_v); - - // Deleted: no in-place value constructor — T& cannot be constructed in-place - template - constexpr expected(in_place_t, Args&&...) = BEMAN_EXPECTED_DELETE_MSG( - "expected: no in-place value constructor; T& cannot be constructed " - "in-place — pass a U convertible to T&"); - - // Value constructor — takes U that can bind to T& - template - requires(!is_same_v, in_place_t> && - !is_same_v, expected> && - !@\exposidnc{is-unexpected-specialization}@>::value && - is_constructible_v && !reference_constructs_from_temporary_v) - constexpr explicit(!is_convertible_v) - expected(U&& u) noexcept(is_nothrow_constructible_v); - - // Deleted: binding a temporary to T& creates a dangling reference - template - requires(reference_constructs_from_temporary_v) - constexpr expected(U&&) = - BEMAN_EXPECTED_DELETE_MSG("expected: argument would bind a temporary that " - "dangles; pass an lvalue reference"); - - // Converting constructor from expected (copy) — value-E path - template - requires(!is_reference_v && is_constructible_v && - is_constructible_v && - !reference_constructs_from_temporary_v) - constexpr explicit(!is_convertible_v || !is_convertible_v) - expected(const expected& rhs); - - // Converting constructor from expected (move) — value-E path - template - requires(!is_reference_v && is_constructible_v && - is_constructible_v && !reference_constructs_from_temporary_v) - constexpr explicit(!is_convertible_v || !is_convertible_v) - expected(expected&& rhs); - - // Converting constructor from expected (copy/move) — reference-E path: only - // accepts sources whose error type G is itself a reference convertible to E. - template - requires(is_reference_v && is_reference_v && is_constructible_v && - is_convertible_v && !reference_constructs_from_temporary_v) - constexpr explicit(!is_convertible_v || !is_convertible_v) - expected(const expected& rhs); - - template - requires(is_reference_v && is_reference_v && is_constructible_v && - is_convertible_v && !reference_constructs_from_temporary_v) - constexpr explicit(!is_convertible_v || !is_convertible_v) - expected(expected&& rhs); - - // Constructor from unexpected const& / && — value-E path - template - requires(!is_reference_v && is_constructible_v) - constexpr explicit(!is_convertible_v) expected(const unexpected& e); - - template - requires(!is_reference_v && is_constructible_v) - constexpr explicit(!is_convertible_v) expected(unexpected&& e); - - // Constructor from unexpected — reference-E path. Allowed only when G is itself a - // reference, i.e. the source unexpected holds a reference to an external object, so - // binding E& to e.error() cannot dangle regardless of the source's value category. No - // const_cast is needed. - template - requires(is_reference_v && is_reference_v && is_constructible_v && - !reference_constructs_from_temporary_v) - constexpr explicit(!is_convertible_v) expected(const unexpected& e) noexcept; - - template - requires(is_reference_v && is_reference_v && is_constructible_v && - !reference_constructs_from_temporary_v) - constexpr explicit(!is_convertible_v) expected(unexpected&& e) noexcept; - - // Deleted for reference E with value G: the referent lives inside the unexpected - // object, so binding E& to it would dangle once a temporary source is destroyed. Use - // (unexpect, lvalue), or an unexpected holding an external object, instead. - template - requires(is_reference_v && !is_reference_v) - constexpr expected(const unexpected&) = BEMAN_EXPECTED_DELETE_MSG( - "expected: cannot construct from unexpected; the value would " - "dangle — use unexpected"); - - template - requires(is_reference_v && !is_reference_v) - constexpr expected(unexpected&&) = BEMAN_EXPECTED_DELETE_MSG( - "expected: cannot construct from unexpected; the value would " - "dangle — use unexpected"); - - // In-place constructor for error - template - requires(is_constructible_v && !@\exposidnc{unexpect-dangles-v}@) - constexpr explicit expected(unexpect_t, Args&&... args); - - // Deleted: single argument would bind E& to a temporary — dangling prevention - template - requires(@\exposidnc{unexpect-dangles-v}@) - constexpr expected(unexpect_t, Args&&...) = - BEMAN_EXPECTED_DELETE_MSG("expected: unexpect argument would bind a " - "temporary that dangles; pass an lvalue reference"); - - // Deleted catch-all: reference E, argument neither constructible nor a dangling case - template - requires(is_reference_v && !is_constructible_v && - !@\exposidnc{unexpect-dangles-v}@) - constexpr expected(unexpect_t, Args&&...) = BEMAN_EXPECTED_DELETE_MSG( - "expected: no viable conversion from the given argument(s) to E&"); - - // In-place constructor for error with initializer_list - template - requires(!is_reference_v && is_constructible_v&, Args...>) - constexpr explicit expected(unexpect_t, initializer_list il, Args&&... args); - - template - requires is_reference_v - constexpr expected(unexpect_t, initializer_list, Args&&...) = - BEMAN_EXPECTED_DELETE_MSG("expected: initializer-list error construction " - "cannot bind a reference; pass an lvalue reference"); - - // ------------------------------------------------------------------------- - // Destructor - // ------------------------------------------------------------------------- - - constexpr ~expected() - requires is_trivially_destructible_v - = default; - - constexpr ~expected() - requires(!is_trivially_destructible_v); - - // ------------------------------------------------------------------------- - // Assignment (rebind semantics) - // ------------------------------------------------------------------------- - - // Copy assignment (trivial path) - constexpr expected& operator=(const expected&) - requires(is_trivially_copy_constructible_v && - is_trivially_copy_assignable_v && is_trivially_destructible_v) - = default; - - // Copy assignment (non-trivial path) - constexpr expected& operator=(const expected& rhs) noexcept( - is_nothrow_copy_constructible_v && is_nothrow_copy_assignable_v) - requires((is_reference_v || - (is_copy_constructible_v && is_copy_assignable_v)) && - !(is_trivially_copy_constructible_v && - is_trivially_copy_assignable_v && is_trivially_destructible_v)); - - // Move assignment (trivial path) - constexpr expected& operator=(expected&&) noexcept - requires(is_trivially_move_constructible_v && - is_trivially_move_assignable_v && is_trivially_destructible_v) - = default; - - // Move assignment (non-trivial path) - constexpr expected& operator=(expected&& rhs) noexcept( - is_nothrow_move_constructible_v && is_nothrow_move_assignable_v) - requires((is_reference_v || - (is_move_constructible_v && is_move_assignable_v)) && - !(is_trivially_move_constructible_v && - is_trivially_move_assignable_v && is_trivially_destructible_v)); - - // Rebind reference from lvalue - template - requires(!is_same_v, expected> && - !@\exposidnc{is-unexpected-specialization}@>::value && - is_constructible_v && !reference_constructs_from_temporary_v) - constexpr expected& operator=(U&& u); - - // Assignment from unexpected — value-E path - template - requires(!is_reference_v && is_constructible_v && - is_assignable_v) - constexpr expected& operator=(const unexpected& e); - - template - requires(!is_reference_v && is_constructible_v && is_assignable_v) - constexpr expected& operator=(unexpected&& e); - - // Rebinding assignment for reference E from reference G — binds unex_ to the external - // referent. - template - requires(is_reference_v && is_reference_v && is_constructible_v && - !reference_constructs_from_temporary_v) - constexpr expected& operator=(const unexpected& e); - - template - requires(is_reference_v && is_reference_v && is_constructible_v && - !reference_constructs_from_temporary_v) - constexpr expected& operator=(unexpected&& e); - - // Deleted for reference E with value G: would rebind E& to unexpected's temporary - // storage. - template - requires(is_reference_v && !is_reference_v) - constexpr expected& operator=(const unexpected&) = BEMAN_EXPECTED_DELETE_MSG( - "expected: cannot assign from unexpected; the value would dangle — " - "use unexpected"); - - template - requires(is_reference_v && !is_reference_v) - constexpr expected& operator=(unexpected&&) = BEMAN_EXPECTED_DELETE_MSG( - "expected: cannot assign from unexpected; the value would dangle — " - "use unexpected"); - - // emplace — rebind the reference - template - requires(is_constructible_v && !reference_constructs_from_temporary_v) - constexpr T& emplace(U&& u) noexcept(is_nothrow_constructible_v); - - // ------------------------------------------------------------------------- - // Swap - // ------------------------------------------------------------------------- - - constexpr void swap(expected& rhs) noexcept(is_nothrow_move_constructible_v && - (is_reference_v || - is_nothrow_swappable_v)) - requires((is_reference_v || is_swappable_v) && is_move_constructible_v); - - friend constexpr void swap(expected& x, expected& y) noexcept(noexcept(x.swap(y))) - requires((is_reference_v || is_swappable_v) && is_move_constructible_v); - - // ------------------------------------------------------------------------- - // 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() &&; - - // error() — shallow const: always returns E& regardless of const on expected - constexpr const E& error() const& noexcept; - constexpr E& error() & noexcept; - constexpr const E&& error() const&& noexcept; - constexpr E&& error() && noexcept; - - template> - requires(is_object_v && !is_array_v) - constexpr remove_cv_t value_or(U&& def) const; - - // Constraints spell error_value_type as its underlying trait expression rather than - // the member typedef: clang (through 22) fails to match an out-of-line constrained - // member of a partial specialization when the requires-clause names a member typedef - // of the class. - template - requires(is_copy_constructible_v>> && - is_convertible_v>>) - constexpr @\exposidnc{error-value-type}@ error_or(G&& def) const&; - - template - requires(is_move_constructible_v>> && - is_convertible_v>>) - constexpr @\exposidnc{error-value-type}@ error_or(G&& def) &&; - - // ------------------------------------------------------------------------- - // Monadic operations - // ------------------------------------------------------------------------- - - template - requires is_constructible_v - constexpr auto and_then(F&& f) &; - template - requires is_constructible_v - constexpr auto and_then(F&& f) &&; - template - requires is_constructible_v - constexpr auto and_then(F&& f) const&; - template - requires is_constructible_v - 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&&; - - // transform: f receives T& (value); error propagates as E; result is expected - template - requires is_constructible_v - constexpr auto transform(F&& f) &; - template - requires is_constructible_v - constexpr auto transform(F&& f) &&; - template - requires is_constructible_v - constexpr auto transform(F&& f) const&; - template - requires is_constructible_v - constexpr auto transform(F&& f) const&&; - - // transform_error: f receives E; value propagates as T&; result is expected - 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&&; - - // ------------------------------------------------------------------------- - // Equality operators (hidden friends) - // ------------------------------------------------------------------------- - - template - requires(!is_void_v) - friend constexpr bool operator==(const expected& x, const expected& y); - - template - requires(!@\exposidnc{is-expected-specialization}@::value) - friend constexpr bool operator==(const expected& x, const T2& val); - - template - friend constexpr bool operator==(const expected& x, const unexpected& e); - -private: - bool @\exposidnc{has-val}@; // exposition only - union { - T* @\exposidnc{val}@; // exposition only - unexpected @\exposidnc{unex}@; // exposition only +namespace std { + template + class @\libglobal{expected}@ { + private: + using @\exposidnc{error-value-type}@ = remove_cv_t>; // exposition only + + public: + using @\libmember{value_type}{expected}@ = T&; + using @\libmember{error_type}{expected}@ = E; + using @\libmember{unexpected_type}{expected}@ = unexpected; + + template using rebind = expected; + + // ------------------------------------------------------------------------- + // Constructors + // ------------------------------------------------------------------------- + + expected() = BEMAN_EXPECTED_DELETE_MSG( + "expected: no default constructor; T& cannot be null"); + + // Copy constructor (trivial path). Unconstrained; see the primary + // template's copy constructor for why. + constexpr expected(const expected&) = default; + + // Copy constructor (non-trivial path) + constexpr expected(const expected& rhs) noexcept(is_nothrow_copy_constructible_v) + requires(is_copy_constructible_v && !is_trivially_copy_constructible_v); + + // Move constructor (trivial path). Unconstrained; no explicit noexcept. + constexpr expected(expected&&) = default; + + // Move constructor (non-trivial path) + constexpr expected(expected&& rhs) noexcept(is_nothrow_move_constructible_v) + requires(is_move_constructible_v && !is_trivially_move_constructible_v); + + // Deleted: no in-place value constructor — T& cannot be constructed in-place + template + constexpr expected(in_place_t, Args&&...) = BEMAN_EXPECTED_DELETE_MSG( + "expected: no in-place value constructor; T& cannot be constructed " + "in-place — pass a U convertible to T&"); + + // Value constructor — takes U that can bind to T& + template + requires(!is_same_v, in_place_t> && + !is_same_v, expected> && + !@\exposidnc{is-unexpected-specialization}@>::value && + is_constructible_v && !reference_constructs_from_temporary_v) + constexpr explicit(!is_convertible_v) + expected(U&& u) noexcept(is_nothrow_constructible_v); + + // Deleted: binding a temporary to T& creates a dangling reference + template + requires(reference_constructs_from_temporary_v) + constexpr expected(U&&) = + BEMAN_EXPECTED_DELETE_MSG("expected: argument would bind a temporary that " + "dangles; pass an lvalue reference"); + + // Converting constructor from expected (copy) — value-E path + template + requires(!is_reference_v && is_constructible_v && + is_constructible_v && + !reference_constructs_from_temporary_v) + constexpr explicit(!is_convertible_v || !is_convertible_v) + expected(const expected& rhs); + + // Converting constructor from expected (move) — value-E path + template + requires(!is_reference_v && is_constructible_v && + is_constructible_v && !reference_constructs_from_temporary_v) + constexpr explicit(!is_convertible_v || !is_convertible_v) + expected(expected&& rhs); + + // Converting constructor from expected (copy/move) — reference-E path: only + // accepts sources whose error type G is itself a reference convertible to E. + template + requires(is_reference_v && is_reference_v && is_constructible_v && + is_convertible_v && !reference_constructs_from_temporary_v) + constexpr explicit(!is_convertible_v || !is_convertible_v) + expected(const expected& rhs); + + template + requires(is_reference_v && is_reference_v && is_constructible_v && + is_convertible_v && !reference_constructs_from_temporary_v) + constexpr explicit(!is_convertible_v || !is_convertible_v) + expected(expected&& rhs); + + // Constructor from unexpected const& / && — value-E path + template + requires(!is_reference_v && is_constructible_v) + constexpr explicit(!is_convertible_v) expected(const unexpected& e); + + template + requires(!is_reference_v && is_constructible_v) + constexpr explicit(!is_convertible_v) expected(unexpected&& e); + + // Constructor from unexpected — reference-E path. Allowed only when G is itself a + // reference, i.e. the source unexpected holds a reference to an external object, so + // binding E& to e.error() cannot dangle regardless of the source's value category. No + // const_cast is needed. + template + requires(is_reference_v && is_reference_v && is_constructible_v && + !reference_constructs_from_temporary_v) + constexpr explicit(!is_convertible_v) expected(const unexpected& e) noexcept; + + template + requires(is_reference_v && is_reference_v && is_constructible_v && + !reference_constructs_from_temporary_v) + constexpr explicit(!is_convertible_v) expected(unexpected&& e) noexcept; + + // Deleted for reference E with value G: the referent lives inside the unexpected + // object, so binding E& to it would dangle once a temporary source is destroyed. Use + // (unexpect, lvalue), or an unexpected holding an external object, instead. + template + requires(is_reference_v && !is_reference_v) + constexpr expected(const unexpected&) = BEMAN_EXPECTED_DELETE_MSG( + "expected: cannot construct from unexpected; the value would " + "dangle — use unexpected"); + + template + requires(is_reference_v && !is_reference_v) + constexpr expected(unexpected&&) = BEMAN_EXPECTED_DELETE_MSG( + "expected: cannot construct from unexpected; the value would " + "dangle — use unexpected"); + + // In-place constructor for error + template + requires(is_constructible_v && !@\exposidnc{unexpect-dangles-v}@) + constexpr explicit expected(unexpect_t, Args&&... args); + + // Deleted: single argument would bind E& to a temporary — dangling prevention + template + requires(@\exposidnc{unexpect-dangles-v}@) + constexpr expected(unexpect_t, Args&&...) = + BEMAN_EXPECTED_DELETE_MSG("expected: unexpect argument would bind a " + "temporary that dangles; pass an lvalue reference"); + + // Deleted catch-all: reference E, argument neither constructible nor a dangling case + template + requires(is_reference_v && !is_constructible_v && + !@\exposidnc{unexpect-dangles-v}@) + constexpr expected(unexpect_t, Args&&...) = BEMAN_EXPECTED_DELETE_MSG( + "expected: no viable conversion from the given argument(s) to E&"); + + // In-place constructor for error with initializer_list + template + requires(!is_reference_v && is_constructible_v&, Args...>) + constexpr explicit expected(unexpect_t, initializer_list il, Args&&... args); + + template + requires is_reference_v + constexpr expected(unexpect_t, initializer_list, Args&&...) = + BEMAN_EXPECTED_DELETE_MSG("expected: initializer-list error construction " + "cannot bind a reference; pass an lvalue reference"); + + // ------------------------------------------------------------------------- + // Destructor + // ------------------------------------------------------------------------- + + constexpr ~expected() + requires is_trivially_destructible_v + = default; + + constexpr ~expected() + requires(!is_trivially_destructible_v); + + // ------------------------------------------------------------------------- + // Assignment (rebind semantics) + // ------------------------------------------------------------------------- + + // Copy assignment (trivial path) + constexpr expected& operator=(const expected&) + requires(is_trivially_copy_constructible_v && + is_trivially_copy_assignable_v && is_trivially_destructible_v) + = default; + + // Copy assignment (non-trivial path) + constexpr expected& operator=(const expected& rhs) noexcept( + is_nothrow_copy_constructible_v && is_nothrow_copy_assignable_v) + requires((is_reference_v || + (is_copy_constructible_v && is_copy_assignable_v)) && + !(is_trivially_copy_constructible_v && + is_trivially_copy_assignable_v && is_trivially_destructible_v)); + + // Move assignment (trivial path) + constexpr expected& operator=(expected&&) noexcept + requires(is_trivially_move_constructible_v && + is_trivially_move_assignable_v && is_trivially_destructible_v) + = default; + + // Move assignment (non-trivial path) + constexpr expected& operator=(expected&& rhs) noexcept( + is_nothrow_move_constructible_v && is_nothrow_move_assignable_v) + requires((is_reference_v || + (is_move_constructible_v && is_move_assignable_v)) && + !(is_trivially_move_constructible_v && + is_trivially_move_assignable_v && is_trivially_destructible_v)); + + // Rebind reference from lvalue + template + requires(!is_same_v, expected> && + !@\exposidnc{is-unexpected-specialization}@>::value && + is_constructible_v && !reference_constructs_from_temporary_v) + constexpr expected& operator=(U&& u); + + // Assignment from unexpected — value-E path + template + requires(!is_reference_v && is_constructible_v && + is_assignable_v) + constexpr expected& operator=(const unexpected& e); + + template + requires(!is_reference_v && is_constructible_v && is_assignable_v) + constexpr expected& operator=(unexpected&& e); + + // Rebinding assignment for reference E from reference G — binds unex_ to the external + // referent. + template + requires(is_reference_v && is_reference_v && is_constructible_v && + !reference_constructs_from_temporary_v) + constexpr expected& operator=(const unexpected& e); + + template + requires(is_reference_v && is_reference_v && is_constructible_v && + !reference_constructs_from_temporary_v) + constexpr expected& operator=(unexpected&& e); + + // Deleted for reference E with value G: would rebind E& to unexpected's temporary + // storage. + template + requires(is_reference_v && !is_reference_v) + constexpr expected& operator=(const unexpected&) = BEMAN_EXPECTED_DELETE_MSG( + "expected: cannot assign from unexpected; the value would dangle — " + "use unexpected"); + + template + requires(is_reference_v && !is_reference_v) + constexpr expected& operator=(unexpected&&) = BEMAN_EXPECTED_DELETE_MSG( + "expected: cannot assign from unexpected; the value would dangle — " + "use unexpected"); + + // emplace — rebind the reference + template + requires(is_constructible_v && !reference_constructs_from_temporary_v) + constexpr T& emplace(U&& u) noexcept(is_nothrow_constructible_v); + + // ------------------------------------------------------------------------- + // Swap + // ------------------------------------------------------------------------- + + constexpr void swap(expected& rhs) noexcept(is_nothrow_move_constructible_v && + (is_reference_v || + is_nothrow_swappable_v)) + requires((is_reference_v || is_swappable_v) && is_move_constructible_v); + + friend constexpr void swap(expected& x, expected& y) noexcept(noexcept(x.swap(y))) + requires((is_reference_v || is_swappable_v) && is_move_constructible_v); + + // ------------------------------------------------------------------------- + // 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() &&; + + // error() — shallow const: always returns E& regardless of const on expected + constexpr const E& error() const& noexcept; + constexpr E& error() & noexcept; + constexpr const E&& error() const&& noexcept; + constexpr E&& error() && noexcept; + + template> + requires(is_object_v && !is_array_v) + constexpr remove_cv_t value_or(U&& def) const; + + // Constraints spell error_value_type as its underlying trait expression rather than + // the member typedef: clang (through 22) fails to match an out-of-line constrained + // member of a partial specialization when the requires-clause names a member typedef + // of the class. + template + requires(is_copy_constructible_v>> && + is_convertible_v>>) + constexpr @\exposidnc{error-value-type}@ error_or(G&& def) const&; + + template + requires(is_move_constructible_v>> && + is_convertible_v>>) + constexpr @\exposidnc{error-value-type}@ error_or(G&& def) &&; + + // ------------------------------------------------------------------------- + // Monadic operations + // ------------------------------------------------------------------------- + + template + requires is_constructible_v + constexpr auto and_then(F&& f) &; + template + requires is_constructible_v + constexpr auto and_then(F&& f) &&; + template + requires is_constructible_v + constexpr auto and_then(F&& f) const&; + template + requires is_constructible_v + 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&&; + + // transform: f receives T& (value); error propagates as E; result is expected + template + requires is_constructible_v + constexpr auto transform(F&& f) &; + template + requires is_constructible_v + constexpr auto transform(F&& f) &&; + template + requires is_constructible_v + constexpr auto transform(F&& f) const&; + template + requires is_constructible_v + constexpr auto transform(F&& f) const&&; + + // transform_error: f receives E; value propagates as T&; result is expected + 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&&; + + // ------------------------------------------------------------------------- + // Equality operators (hidden friends) + // ------------------------------------------------------------------------- + + template + requires(!is_void_v) + friend constexpr bool operator==(const expected& x, const expected& y); + + template + requires(!@\exposidnc{is-expected-specialization}@::value) + friend constexpr bool operator==(const expected& x, const T2& val); + + template + friend constexpr bool operator==(const expected& x, const unexpected& e); + + private: + bool @\exposidnc{has-val}@; // exposition only + union { + T* @\exposidnc{val}@; // exposition only + unexpected @\exposidnc{unex}@; // exposition only + }; }; -}; +} \end{codeblock} \begin{itemdescr} diff --git a/papers/wording/fragments/expected.unexpected.tex b/papers/wording/fragments/expected.unexpected.tex index 60b41e0..dc005b7 100644 --- a/papers/wording/fragments/expected.unexpected.tex +++ b/papers/wording/fragments/expected.unexpected.tex @@ -3,50 +3,52 @@ \rSec3[expected.un.general]{General} \begin{codeblock} -template -class @\libglobal{unexpected}@ { - // [expected.un.general] para 2: ill-formed instantiations - -public: - constexpr unexpected(const unexpected&) = default; - constexpr unexpected(unexpected&&) = default; - - template - requires(!is_same_v, unexpected> && - !is_same_v, in_place_t> && is_constructible_v) - constexpr explicit unexpected(Err&& e) noexcept(is_nothrow_constructible_v); - - template - requires is_constructible_v - constexpr explicit unexpected(in_place_t, Args&&... args) noexcept( - is_nothrow_constructible_v); - - template - requires is_constructible_v&, Args...> - constexpr explicit unexpected( - in_place_t, initializer_list il, - Args&&... args) noexcept(is_nothrow_constructible_v&, - 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(is_nothrow_swappable_v); - - template - friend constexpr bool operator==(const unexpected& x, const unexpected& y); - - friend constexpr void swap(unexpected& x, unexpected& y) noexcept(noexcept(x.swap(y))) - requires is_swappable_v; - -private: - E @\exposidnc{unex}@; // exposition only -}; +namespace std { + template + class @\libglobal{unexpected}@ { + // [expected.un.general] para 2: ill-formed instantiations + + public: + constexpr unexpected(const unexpected&) = default; + constexpr unexpected(unexpected&&) = default; + + template + requires(!is_same_v, unexpected> && + !is_same_v, in_place_t> && is_constructible_v) + constexpr explicit unexpected(Err&& e) noexcept(is_nothrow_constructible_v); + + template + requires is_constructible_v + constexpr explicit unexpected(in_place_t, Args&&... args) noexcept( + is_nothrow_constructible_v); + + template + requires is_constructible_v&, Args...> + constexpr explicit unexpected( + in_place_t, initializer_list il, + Args&&... args) noexcept(is_nothrow_constructible_v&, + 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(is_nothrow_swappable_v); + + template + friend constexpr bool operator==(const unexpected& x, const unexpected& y); + + friend constexpr void swap(unexpected& x, unexpected& y) noexcept(noexcept(x.swap(y))) + requires is_swappable_v; + + private: + E @\exposidnc{unex}@; // exposition only + }; +} \end{codeblock} \begin{itemdescr} @@ -216,31 +218,33 @@ \rSec3[expected.un.ref]{Partial specialization 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; +namespace std { + 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 unexpected& operator=(const unexpected&) = default; + constexpr unexpected& operator=(unexpected&&) = default; - constexpr E& error() const noexcept; + constexpr E& error() const noexcept; - constexpr void swap(unexpected& other) noexcept; + constexpr void swap(unexpected& other) noexcept; - template - friend constexpr bool operator==(const unexpected&, const unexpected&); + template + friend constexpr bool operator==(const unexpected&, const unexpected&); - friend constexpr void swap(unexpected& x, unexpected& y) noexcept; + friend constexpr void swap(unexpected& x, unexpected& y) noexcept; -private: - E* unex; // exposition only -}; + private: + E* unex; // exposition only + }; +} \end{codeblock} \begin{itemdescr} diff --git a/papers/wording/fragments/expected.void.tex b/papers/wording/fragments/expected.void.tex index 7f5ee68..eb981a4 100644 --- a/papers/wording/fragments/expected.void.tex +++ b/papers/wording/fragments/expected.void.tex @@ -3,326 +3,327 @@ \rSec3[expected.void.general]{General} \begin{codeblock} -template -class @\libglobal{expected}@ { -private: - using @\exposidnc{error-value-type}@ = remove_cv_t>; // exposition only - -public: - using @\libmember{value_type}{expected}@ = void; - using @\libmember{error_type}{expected}@ = E; - using @\libmember{unexpected_type}{expected}@ = unexpected; - - template using rebind = expected; - - // ------------------------------------------------------------------------- - // [expected.void.cons] Constructors - // ------------------------------------------------------------------------- - - constexpr expected() noexcept; - - // Unconstrained trivial-path candidate: see the primary template's copy - // constructor for why (the sole declaration when E is not copy - // constructible at all; subsumed by the non-trivial path otherwise). - - constexpr expected(const expected& rhs) noexcept(is_nothrow_copy_constructible_v) - requires(is_copy_constructible_v && !is_trivially_copy_constructible_v); - - // Unconstrained; no explicit noexcept — see the primary template's move - // constructor for why. - - constexpr expected(expected&& rhs) noexcept(is_nothrow_move_constructible_v) - requires(is_move_constructible_v && !is_trivially_move_constructible_v); - - // Converting constructor from expected where is_void_v. Excludes U,G exactly - // matching this class's own void,E (the real copy/move constructors already handle - // that case) — instantiating this template for the self-referential case would - // otherwise probe unexpected's constructibility from this very class, which some - // standard library implementations of reference_constructs_from_temporary_v resolve - // as a circular constraint. - template - requires(is_void_v && !is_reference_v && !is_same_v && - is_constructible_v && - !is_constructible_v, expected&> && - !is_constructible_v, expected&&> && - !is_constructible_v, const expected&> && - !is_constructible_v, const expected&&>) - constexpr explicit(!is_convertible_v) - expected(const expected& rhs); - - template - requires(is_void_v && !is_reference_v && !is_same_v && - is_constructible_v && - !is_constructible_v, expected&> && - !is_constructible_v, expected&&> && - !is_constructible_v, const expected&> && - !is_constructible_v, const expected&&>) - constexpr explicit(!is_convertible_v) expected(expected&& rhs); - - // Constructor from unexpected const& / && — value-E path - template - requires(!is_reference_v && is_constructible_v) - constexpr explicit(!is_convertible_v) expected(const unexpected& e); - - template - requires(!is_reference_v && is_constructible_v) - constexpr explicit(!is_convertible_v) expected(unexpected&& e); - - // Constructor from unexpected — reference-E path. Allowed only when G is itself a - // reference, so e.error() refers to an external object and binding E& cannot dangle. - // No const_cast needed. - template - requires(is_reference_v && is_reference_v && is_constructible_v && - !reference_constructs_from_temporary_v) - constexpr explicit(!is_convertible_v) expected(const unexpected& e) noexcept; - - template - requires(is_reference_v && is_reference_v && is_constructible_v && - !reference_constructs_from_temporary_v) - constexpr explicit(!is_convertible_v) expected(unexpected&& e) noexcept; - - // Deleted for reference E with value G: the referent lives inside the temporary - // unexpected, so binding E& to it would dangle once the source is destroyed. Use - // (unexpect, lvalue) instead. - template - requires(is_reference_v && !is_reference_v) - constexpr expected(const unexpected&) = BEMAN_EXPECTED_DELETE_MSG( - "expected: cannot construct from unexpected; the value would " - "dangle — use unexpected"); - - template - requires(is_reference_v && !is_reference_v) - constexpr expected(unexpected&&) = BEMAN_EXPECTED_DELETE_MSG( - "expected: cannot construct from unexpected; the value would " - "dangle — use unexpected"); - - // In-place constructor for value (no args, just marks has-value) - constexpr explicit expected(in_place_t) noexcept; - - // In-place constructor for error - template - requires(is_constructible_v && !@\exposidnc{unexpect-dangles-v}@) - constexpr explicit expected(unexpect_t, Args&&... args); - - // Deleted: single argument would bind E& to a temporary — dangling prevention - template - requires(@\exposidnc{unexpect-dangles-v}@) - constexpr expected(unexpect_t, Args&&...) = - BEMAN_EXPECTED_DELETE_MSG("expected: unexpect argument would bind a " - "temporary that dangles; pass an lvalue reference"); - - // Deleted catch-all: reference E, argument neither constructible nor a dangling case - // (e.g. binding a non-const E& from a const lvalue). - template - requires(is_reference_v && !is_constructible_v && - !@\exposidnc{unexpect-dangles-v}@) - constexpr expected(unexpect_t, Args&&...) = BEMAN_EXPECTED_DELETE_MSG( - "expected: no viable conversion from the given argument(s) to E&"); - - // In-place constructor for error with initializer_list - template - requires(!is_reference_v && is_constructible_v&, Args...>) - constexpr explicit expected(unexpect_t, initializer_list il, Args&&... args); - - template - requires is_reference_v - constexpr expected(unexpect_t, initializer_list, Args&&...) = - BEMAN_EXPECTED_DELETE_MSG( - "expected: initializer-list error construction cannot bind a " - "reference; pass an lvalue reference"); - - // Converting constructor from expected — reference-E path only. G is itself - // a reference to an external object, so binding E& to it cannot dangle regardless of - // the source's value category, provided the reference conversion itself does not - // materialize a temporary (e.g. a base-from-derived or qualification conversion is - // fine; a user-defined conversion that returns by value is not). Mirrors the - // unexpected reference-E path above. - template - requires(is_reference_v && is_convertible_v && - !reference_constructs_from_temporary_v) - constexpr explicit(!is_convertible_v) expected(const expected& rhs); - - template - requires(is_reference_v && is_convertible_v && - !reference_constructs_from_temporary_v) - constexpr explicit(!is_convertible_v) expected(expected&& rhs); - - // ------------------------------------------------------------------------- - // [expected.void.dtor] Destructor - // ------------------------------------------------------------------------- - - constexpr ~expected() - requires(!is_trivially_destructible_v); - - // ------------------------------------------------------------------------- - // [expected.void.assign] Assignment - // ------------------------------------------------------------------------- - - // Copy assignment (trivial path) - - // Copy assignment (non-trivial path) - constexpr expected& operator=(const expected& rhs) noexcept( - is_nothrow_copy_constructible_v && is_nothrow_copy_assignable_v) - requires((is_reference_v || - (is_copy_constructible_v && is_copy_assignable_v)) && - !(is_trivially_copy_constructible_v && - is_trivially_copy_assignable_v && is_trivially_destructible_v)); - - // Move assignment (trivial path) - - // Move assignment (non-trivial path) - constexpr expected& operator=(expected&& rhs) noexcept( - is_nothrow_move_constructible_v && is_nothrow_move_assignable_v) - requires((is_reference_v || - (is_move_constructible_v && is_move_assignable_v)) && - !(is_trivially_move_constructible_v && - is_trivially_move_assignable_v && is_trivially_destructible_v)); - - // Assignment from unexpected — value-E path. - template - requires(!is_reference_v && is_constructible_v && - is_assignable_v) - constexpr expected& operator=(const unexpected& e); - - template - requires(!is_reference_v && is_constructible_v && is_assignable_v) - constexpr expected& operator=(unexpected&& e); - - // Rebinding assignment for reference E from reference G — binds unex_ to the external - // referent. - template - requires(is_reference_v && is_reference_v && is_constructible_v && - !reference_constructs_from_temporary_v) - constexpr expected& operator=(const unexpected& e); - - template - requires(is_reference_v && is_reference_v && is_constructible_v && - !reference_constructs_from_temporary_v) - constexpr expected& operator=(unexpected&& e); - - // Deleted for reference E with value G: would bind E& to storage inside the temporary - // unexpected. - template - requires(is_reference_v && !is_reference_v) - constexpr expected& operator=(const unexpected&) = BEMAN_EXPECTED_DELETE_MSG( - "expected: cannot assign from unexpected; the value would dangle " - "— use unexpected"); - - template - requires(is_reference_v && !is_reference_v) - constexpr expected& operator=(unexpected&&) = BEMAN_EXPECTED_DELETE_MSG( - "expected: cannot assign from unexpected; the value would dangle " - "— use unexpected"); - - constexpr void emplace() noexcept; - - // ------------------------------------------------------------------------- - // [expected.void.swap] Swap - // ------------------------------------------------------------------------- - - constexpr void swap(expected& rhs) noexcept(is_nothrow_move_constructible_v && - (is_reference_v || - is_nothrow_swappable_v)) - requires((is_reference_v || is_swappable_v) && is_move_constructible_v); - - friend constexpr void swap(expected& x, expected& y) noexcept(noexcept(x.swap(y))) - requires((is_reference_v || is_swappable_v) && is_move_constructible_v); - - // ------------------------------------------------------------------------- - // [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&; - constexpr void value() &&; - - // error() — shallow const for reference E: always returns E& regardless of const on - // expected - constexpr const E& error() const& noexcept; - constexpr E& error() & noexcept; - constexpr const E&& error() const&& noexcept; - constexpr E&& error() && noexcept; - - template - requires(is_copy_constructible_v>> && - is_convertible_v>>) - constexpr @\exposidnc{error-value-type}@ error_or(G&& def) const&; - - template - requires(is_move_constructible_v>> && - is_convertible_v>>) - constexpr @\exposidnc{error-value-type}@ error_or(G&& def) &&; - - // Deleted: value_or is not available for void expected. Gated to reference E only so - // that, for value E, no value_or overload is declared at all (there is nothing to - // delete against). - template - requires is_reference_v - constexpr void value_or(U&&) const = BEMAN_EXPECTED_DELETE_MSG( - "expected: value_or is not defined for void value_type; there is no " - "value to fall back from — use has_value()/error()"); - - // ------------------------------------------------------------------------- - // [expected.void.monadic] Monadic operations - // ------------------------------------------------------------------------- - - template - requires is_constructible_v - constexpr auto and_then(F&& f) &; - template - requires is_constructible_v - constexpr auto and_then(F&& f) &&; - template - requires is_constructible_v - constexpr auto and_then(F&& f) const&; - template - requires is_constructible_v - 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 - requires is_constructible_v - constexpr auto transform(F&& f) &; - template - requires is_constructible_v - constexpr auto transform(F&& f) &&; - template - requires is_constructible_v - constexpr auto transform(F&& f) const&; - template - requires is_constructible_v - 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&&; - - // ------------------------------------------------------------------------- - // [expected.void.eq] Equality operators (hidden friends) - // ------------------------------------------------------------------------- - - template - requires is_void_v - friend constexpr bool operator==(const expected& x, const expected& y); - - template - friend constexpr bool operator==(const expected& x, const unexpected& e); - -private: - bool @\exposidnc{has-val}@; // exposition only - union { - unexpected @\exposidnc{unex}@; // exposition only +namespace std { + template + class @\libglobal{expected}@ { + private: + using @\exposidnc{error-value-type}@ = remove_cv_t>; // exposition only + + public: + using @\libmember{value_type}{expected}@ = void; + using @\libmember{error_type}{expected}@ = E; + using @\libmember{unexpected_type}{expected}@ = unexpected; + + template using rebind = expected; + + // ------------------------------------------------------------------------- + // [expected.void.cons] Constructors + // ------------------------------------------------------------------------- + + constexpr expected() noexcept; + + // Unconstrained trivial-path candidate: see the primary template's copy + // constructor for why (the sole declaration when E is not copy + // constructible at all; subsumed by the non-trivial path otherwise). + + constexpr expected(const expected& rhs) noexcept(is_nothrow_copy_constructible_v) + requires(is_copy_constructible_v && !is_trivially_copy_constructible_v); + + // Unconstrained; no explicit noexcept — see the primary template's move + // constructor for why. + + constexpr expected(expected&& rhs) noexcept(is_nothrow_move_constructible_v) + requires(is_move_constructible_v && !is_trivially_move_constructible_v); + + // Converting constructor from expected where is_void_v. Excludes U,G exactly + // matching this class's own void,E (the real copy/move constructors already handle + // that case) — instantiating this template for the self-referential case would + // otherwise probe unexpected's constructibility from this very class, which some + // standard library implementations of reference_constructs_from_temporary_v resolve + // as a circular constraint. + template + requires(is_void_v && !is_reference_v && !is_same_v && + is_constructible_v && + !is_constructible_v, expected&> && + !is_constructible_v, expected&&> && + !is_constructible_v, const expected&> && + !is_constructible_v, const expected&&>) + constexpr explicit(!is_convertible_v) + expected(const expected& rhs); + + template + requires(is_void_v && !is_reference_v && !is_same_v && + is_constructible_v && + !is_constructible_v, expected&> && + !is_constructible_v, expected&&> && + !is_constructible_v, const expected&> && + !is_constructible_v, const expected&&>) + constexpr explicit(!is_convertible_v) expected(expected&& rhs); + + // Constructor from unexpected const& / && — value-E path + template + requires(!is_reference_v && is_constructible_v) + constexpr explicit(!is_convertible_v) expected(const unexpected& e); + + template + requires(!is_reference_v && is_constructible_v) + constexpr explicit(!is_convertible_v) expected(unexpected&& e); + + // Constructor from unexpected — reference-E path. Allowed only when G is itself a + // reference, so e.error() refers to an external object and binding E& cannot dangle. + // No const_cast needed. + template + requires(is_reference_v && is_reference_v && is_constructible_v && + !reference_constructs_from_temporary_v) + constexpr explicit(!is_convertible_v) expected(const unexpected& e) noexcept; + + template + requires(is_reference_v && is_reference_v && is_constructible_v && + !reference_constructs_from_temporary_v) + constexpr explicit(!is_convertible_v) expected(unexpected&& e) noexcept; + + // Deleted for reference E with value G: the referent lives inside the temporary + // unexpected, so binding E& to it would dangle once the source is destroyed. Use + // (unexpect, lvalue) instead. + template + requires(is_reference_v && !is_reference_v) + constexpr expected(const unexpected&) = BEMAN_EXPECTED_DELETE_MSG( + "expected: cannot construct from unexpected; the value would " + "dangle — use unexpected"); + + template + requires(is_reference_v && !is_reference_v) + constexpr expected(unexpected&&) = BEMAN_EXPECTED_DELETE_MSG( + "expected: cannot construct from unexpected; the value would " + "dangle — use unexpected"); + + // In-place constructor for value (no args, just marks has-value) + constexpr explicit expected(in_place_t) noexcept; + + // In-place constructor for error + template + requires(is_constructible_v && !@\exposidnc{unexpect-dangles-v}@) + constexpr explicit expected(unexpect_t, Args&&... args); + + // Deleted: single argument would bind E& to a temporary — dangling prevention + template + requires(@\exposidnc{unexpect-dangles-v}@) + constexpr expected(unexpect_t, Args&&...) = + BEMAN_EXPECTED_DELETE_MSG("expected: unexpect argument would bind a " + "temporary that dangles; pass an lvalue reference"); + + // Deleted catch-all: reference E, argument neither constructible nor a dangling case + // (e.g. binding a non-const E& from a const lvalue). + template + requires(is_reference_v && !is_constructible_v && + !@\exposidnc{unexpect-dangles-v}@) + constexpr expected(unexpect_t, Args&&...) = BEMAN_EXPECTED_DELETE_MSG( + "expected: no viable conversion from the given argument(s) to E&"); + + // In-place constructor for error with initializer_list + template + requires(!is_reference_v && is_constructible_v&, Args...>) + constexpr explicit expected(unexpect_t, initializer_list il, Args&&... args); + + template + requires is_reference_v + constexpr expected(unexpect_t, initializer_list, Args&&...) = + BEMAN_EXPECTED_DELETE_MSG( + "expected: initializer-list error construction cannot bind a " + "reference; pass an lvalue reference"); + + // Converting constructor from expected — reference-E path only. G is itself + // a reference to an external object, so binding E& to it cannot dangle regardless of + // the source's value category, provided the reference conversion itself does not + // materialize a temporary (e.g. a base-from-derived or qualification conversion is + // fine; a user-defined conversion that returns by value is not). Mirrors the + // unexpected reference-E path above. + template + requires(is_reference_v && is_convertible_v && + !reference_constructs_from_temporary_v) + constexpr explicit(!is_convertible_v) expected(const expected& rhs); + + template + requires(is_reference_v && is_convertible_v && + !reference_constructs_from_temporary_v) + constexpr explicit(!is_convertible_v) expected(expected&& rhs); + + // ------------------------------------------------------------------------- + // [expected.void.dtor] Destructor + // ------------------------------------------------------------------------- + + constexpr ~expected(); + + // ------------------------------------------------------------------------- + // [expected.void.assign] Assignment + // ------------------------------------------------------------------------- + + // Copy assignment (trivial path) + + // Copy assignment (non-trivial path) + constexpr expected& operator=(const expected& rhs) noexcept( + is_nothrow_copy_constructible_v && is_nothrow_copy_assignable_v) + requires((is_reference_v || + (is_copy_constructible_v && is_copy_assignable_v)) && + !(is_trivially_copy_constructible_v && + is_trivially_copy_assignable_v && is_trivially_destructible_v)); + + // Move assignment (trivial path) + + // Move assignment (non-trivial path) + constexpr expected& operator=(expected&& rhs) noexcept( + is_nothrow_move_constructible_v && is_nothrow_move_assignable_v) + requires((is_reference_v || + (is_move_constructible_v && is_move_assignable_v)) && + !(is_trivially_move_constructible_v && + is_trivially_move_assignable_v && is_trivially_destructible_v)); + + // Assignment from unexpected — value-E path. + template + requires(!is_reference_v && is_constructible_v && + is_assignable_v) + constexpr expected& operator=(const unexpected& e); + + template + requires(!is_reference_v && is_constructible_v && is_assignable_v) + constexpr expected& operator=(unexpected&& e); + + // Rebinding assignment for reference E from reference G — binds unex_ to the external + // referent. + template + requires(is_reference_v && is_reference_v && is_constructible_v && + !reference_constructs_from_temporary_v) + constexpr expected& operator=(const unexpected& e); + + template + requires(is_reference_v && is_reference_v && is_constructible_v && + !reference_constructs_from_temporary_v) + constexpr expected& operator=(unexpected&& e); + + // Deleted for reference E with value G: would bind E& to storage inside the temporary + // unexpected. + template + requires(is_reference_v && !is_reference_v) + constexpr expected& operator=(const unexpected&) = BEMAN_EXPECTED_DELETE_MSG( + "expected: cannot assign from unexpected; the value would dangle " + "— use unexpected"); + + template + requires(is_reference_v && !is_reference_v) + constexpr expected& operator=(unexpected&&) = BEMAN_EXPECTED_DELETE_MSG( + "expected: cannot assign from unexpected; the value would dangle " + "— use unexpected"); + + constexpr void emplace() noexcept; + + // ------------------------------------------------------------------------- + // [expected.void.swap] Swap + // ------------------------------------------------------------------------- + + constexpr void swap(expected& rhs) noexcept(is_nothrow_move_constructible_v && + (is_reference_v || + is_nothrow_swappable_v)) + requires((is_reference_v || is_swappable_v) && is_move_constructible_v); + + friend constexpr void swap(expected& x, expected& y) noexcept(noexcept(x.swap(y))) + requires((is_reference_v || is_swappable_v) && is_move_constructible_v); + + // ------------------------------------------------------------------------- + // [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&; + constexpr void value() &&; + + // error() — shallow const for reference E: always returns E& regardless of const on + // expected + constexpr const E& error() const& noexcept; + constexpr E& error() & noexcept; + constexpr const E&& error() const&& noexcept; + constexpr E&& error() && noexcept; + + template + requires(is_copy_constructible_v>> && + is_convertible_v>>) + constexpr @\exposidnc{error-value-type}@ error_or(G&& def) const&; + + template + requires(is_move_constructible_v>> && + is_convertible_v>>) + constexpr @\exposidnc{error-value-type}@ error_or(G&& def) &&; + + // Deleted: value_or is not available for void expected. Gated to reference E only so + // that, for value E, no value_or overload is declared at all (there is nothing to + // delete against). + template + requires is_reference_v + constexpr void value_or(U&&) const = BEMAN_EXPECTED_DELETE_MSG( + "expected: value_or is not defined for void value_type; there is no " + "value to fall back from — use has_value()/error()"); + + // ------------------------------------------------------------------------- + // [expected.void.monadic] Monadic operations + // ------------------------------------------------------------------------- + + template + requires is_constructible_v + constexpr auto and_then(F&& f) &; + template + requires is_constructible_v + constexpr auto and_then(F&& f) &&; + template + requires is_constructible_v + constexpr auto and_then(F&& f) const&; + template + requires is_constructible_v + 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 + requires is_constructible_v + constexpr auto transform(F&& f) &; + template + requires is_constructible_v + constexpr auto transform(F&& f) &&; + template + requires is_constructible_v + constexpr auto transform(F&& f) const&; + template + requires is_constructible_v + 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&&; + + // ------------------------------------------------------------------------- + // [expected.void.eq] Equality operators (hidden friends) + // ------------------------------------------------------------------------- + + template + requires is_void_v + friend constexpr bool operator==(const expected& x, const expected& y); + + template + friend constexpr bool operator==(const expected& x, const unexpected& e); + + private: + bool @\exposidnc{has-val}@; // exposition only + union { + unexpected @\exposidnc{unex}@; // exposition only + }; }; -}; +} \end{codeblock} \begin{itemdescr} @@ -622,10 +623,6 @@ \end{itemdecl} \begin{itemdescr} -\pnum -\constraints -\tcode{is_trivially_destructible_v} is \tcode{false}. - \pnum \effects If \tcode{has_value()} is \tcode{false}, destroys \tcode{unex}.