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/D4280R0.tex b/papers/D4280R0.tex index e760ad5..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} @@ -774,20 +747,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..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 @@ -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 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}.