diff --git a/.gitignore b/.gitignore index 8f0e1c2..b90a49d 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,9 @@ /.emacs.d/elpa/ /.emacs.d/custom.el *.deps + +# specgen writes one *.root.tex per document, holding the exposition-only +# helpers that sit outside every clause. The draft states those inline in the +# clause that uses them, so they are not part of this paper's wording and are +# not committed -- see papers/wording/README.md. +papers/wording/fragments/*.root.tex diff --git a/Makefile b/Makefile index eb40c92..4f1ff44 100755 --- a/Makefile +++ b/Makefile @@ -154,9 +154,87 @@ env: $(foreach v, $(.VARIABLES), $(info $(v) = $($(v)))) .PHONY: papers -papers: +papers: wording $(MAKE) -C papers papers +# ---- Generated [expected] wording ------------------------------------------ +# +# The three annotated headers are one specgen document each and one paper: a +# single invocation renders them together, so --validate sees the paper-wide +# union of documented names and a name specified by a sibling header is not +# foreign. +# +# specgen writes the fragments; this makefile assembles expected.tex from them, +# because the paper's clause order is not the headers' declaration order and +# cannot be. bad_expected_access is the base class of +# bad_expected_access, so the header has to define it first, while the draft +# states the primary template first. $(WORDING_CLAUSES) is where that one +# divergence is written down. + +SPECGEN ?= specgen + +WORDING_HEADERS := $(addprefix include/beman/expected/, \ + unexpected.hpp bad_expected_access.hpp expected.hpp) + +# In the draft's clause order, by stable name. These are the fragments that +# become expected.tex; specgen also writes one *.root.tex per document, holding +# the exposition-only helpers that sit outside every clause. The draft states +# those inline in the clause that uses them -- reinit-expected inside +# [expected.object.assign]'s own intro -- so they are deliberately not part of +# the assembled wording. +WORDING_CLAUSES := \ + expected.unexpected \ + expected.bad \ + expected.bad.void \ + expected.expected \ + expected.void \ + expected.ref + +WORDING_DIR := papers/wording +WORDING_FRAGMENTS := \ + $(addprefix $(WORDING_DIR)/fragments/,$(addsuffix .tex,$(WORDING_CLAUSES))) + +SPECGEN_CLANG_ARGS := -std=c++2c -Iinclude +ifneq ($(SPECGEN_GCC_TOOLCHAIN),) +SPECGEN_CLANG_ARGS += --gcc-toolchain=$(SPECGEN_GCC_TOOLCHAIN) +endif + +# A grouped target (GNU Make 4.3+): one invocation writes all of these, and +# make must not run it once per fragment. +$(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. +$(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 $@ + @echo "Wrote $@" + +.PHONY: wording +wording: $(WORDING_DIR)/expected.tex ## Regenerate papers/wording/ from the annotated headers via specgen + +# 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 +# $(wildcard): a bare glob that matches nothing stays a literal target name, +# and .DEFAULT below would hand it to cmake. Named, not globbed: papers/.deps/ +# is also latexmk's -deps-out directory, and its paths are relative to papers/. +-include $(wildcard papers/.deps/wording.d) + .DEFAULT: $(_build_path)/CMakeCache.txt ## Other targets passed through to cmake $(CMAKE) --build $(_build_path) --config $(CONFIG) --target $@ -- -k 0 diff --git a/include/beman/expected/bad_expected_access.hpp b/include/beman/expected/bad_expected_access.hpp index a7da3e6..15af55f 100644 --- a/include/beman/expected/bad_expected_access.hpp +++ b/include/beman/expected/bad_expected_access.hpp @@ -60,6 +60,7 @@ namespace expected { template class bad_expected_access; +// \rSec2[expected.bad.void]{Class template specialization bad_expected_access} template <> class bad_expected_access : public std::exception { protected: @@ -74,6 +75,11 @@ class bad_expected_access : public std::exception { BEMAN_EXPECTED_CONSTEXPR_EXCEPTION const char* what() const noexcept override; }; +// \rSec2[expected.bad]{Class template bad_expected_access} +//! \remarks The class template `bad_expected_access` defines the type of +//! objects thrown as exceptions to report the situation where an attempt is +//! made to access the value of an `expected` object for which +//! `has_value()` is `false`. template class bad_expected_access : public bad_expected_access { public: @@ -85,40 +91,50 @@ class bad_expected_access : public bad_expected_access { constexpr const E&& error() const&& noexcept; private: + //! \expos E unex; }; // bad_expected_access out-of-line definitions +//! \returns An implementation-defined ntbs, which during constant evaluation +//! is encoded with the ordinary literal encoding (\iref{lex.ccon}). inline BEMAN_EXPECTED_CONSTEXPR_EXCEPTION const char* bad_expected_access::what() const noexcept { return "bad expected access"; } // bad_expected_access out-of-line definitions +//! \effects Initializes `unex` with `std::move(e)`. template BEMAN_EXPECTED_CONSTEXPR_EXCEPTION bad_expected_access::bad_expected_access(E e) : unex(std::move(e)) {} +//! \returns An implementation-defined ntbs, which during constant evaluation +//! is encoded with the ordinary literal encoding (\iref{lex.ccon}). template BEMAN_EXPECTED_CONSTEXPR_EXCEPTION const char* bad_expected_access::what() const noexcept { return "bad expected access"; } +//! \returns `unex`. template constexpr E& bad_expected_access::error() & noexcept { return unex; } +//! \returns `unex`. template constexpr const E& bad_expected_access::error() const& noexcept { return unex; } +//! \returns `std::move(unex)`. template constexpr E&& bad_expected_access::error() && noexcept { return std::move(unex); } +//! \returns `std::move(unex)`. template constexpr const E&& bad_expected_access::error() const&& noexcept { return std::move(unex); diff --git a/include/beman/expected/expected.hpp b/include/beman/expected/expected.hpp index f4c0ad1..c63cc0f 100644 --- a/include/beman/expected/expected.hpp +++ b/include/beman/expected/expected.hpp @@ -56,12 +56,14 @@ namespace expected { namespace detail { +//! \expos template struct is_expected_specialization : std::false_type {}; // forward-declared in primary template below; specializations added after class definition // [expected.object.assign] reinit_expected helper +//! \expos template constexpr void reinit_expected(NewVal& newval, CurVal& oldval, Args&&... args) { if constexpr (std::is_nothrow_constructible_v) { @@ -90,6 +92,7 @@ constexpr void reinit_expected(NewVal& newval, CurVal& oldval, Args&&... args) { // unexpect_dangles_v: true iff constructing expected's error in place from Args... // would bind a reference E to a temporary. False whenever E is not a reference, or arity != 1 // (a reference can only ever bind from a single argument), so it never affects the value-E path. +//! \expos template inline constexpr bool unexpect_dangles_v = false; @@ -105,6 +108,7 @@ namespace detail { template struct is_expected_specialization> : std::true_type {}; +//! \expos template constexpr bool converts_from_any_cvref = std::disjunction_v, std::is_convertible, @@ -116,7 +120,22 @@ constexpr bool converts_from_any_cvref = std::disjunction_v>; } // namespace detail -// [expected.expected], class template expected +// \rSec2[expected.expected]{Class template expected} +// \rSec3[expected.object.general]{General} +//! \mandates A program that instantiates the definition of `expected` +//! with a `T` that is not a valid value type for `expected` (that is, +//! `remove_cv_t` is `void`, or a complete non-array object type other +//! than `in_place_t`, `unexpect_t`, or a specialization of `unexpected`) is +//! ill-formed. A program that instantiates the definition of `expected` with an `E` that is not a valid template argument for `unexpected` +//! is ill-formed. +//! \remarks Any object of type `expected` either contains a value of +//! type `T` or a value of type `E` nested within it. Member `has_val` +//! indicates whether the `expected` object contains an object of type +//! `T`. When `has_value()` is `false`, the error is `unex.error()`. The +//! error is held as an `unexpected`, and not as an `E`, so that `E` may +//! be an lvalue reference type: an `E&` cannot be a union member, whereas +//! `unexpected` holds a pointer to an external object. template class expected { static_assert(!std::is_reference_v, "T must not be a reference (use expected specialization)"); @@ -130,6 +149,7 @@ class expected { static_assert(!std::is_array_v>, "E must not be an array type"); private: + //! \expos using error_value_type = std::remove_cv_t>; public: @@ -153,6 +173,11 @@ class expected { // 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. + //! \at expected.object.cons + //! \effects Direct-non-list-initializes `val` or `unex` (matching + //! `rhs`'s active member) by trivial copy construction. + //! \ensures `rhs.has_value() == this->has_value()`. + //! \remarks This constructor is trivial. constexpr expected(const expected&) = default; // Copy constructor (non-trivial path) @@ -164,6 +189,11 @@ class expected { // 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. + //! \at expected.object.cons + //! \effects Direct-non-list-initializes `val` or `unex` (matching + //! `rhs`'s active member) by trivial move construction. + //! \ensures `rhs.has_value() == this->has_value()`. + //! \remarks This constructor is trivial. constexpr expected(expected&&) = default; // Move constructor (non-trivial path) @@ -242,11 +272,20 @@ class expected { // 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. + //! \at expected.object.cons + //! \group cvt-unexpected-ctor-deleted + //! \remarks When `E` is a reference type, an overload taking + //! `unexpected` for a non-reference `G` is defined as deleted: the + //! referent would live inside the (possibly temporary) source + //! `unexpected` object, and binding `E&` to it would dangle. Use an + //! `unexpected` holding an external object instead. template requires(std::is_reference_v && !std::is_reference_v) constexpr expected(const unexpected&) = BEMAN_EXPECTED_DELETE_MSG( "expected: cannot construct from unexpected; the value would dangle — use unexpected"); + //! \at expected.object.cons + //! \also cvt-unexpected-ctor-deleted template requires(std::is_reference_v && !std::is_reference_v) constexpr expected(unexpected&&) = BEMAN_EXPECTED_DELETE_MSG( @@ -268,6 +307,12 @@ class expected { constexpr explicit expected(unexpect_t, Args&&... args); // Deleted: single argument would bind E& to a temporary — dangling prevention + //! \at expected.object.cons + //! \group unexpect-ctor-deleted + //! \remarks When `E` is a reference type, an overload with the same + //! parameter types is defined as deleted if the single argument would + //! bind `E&` to a temporary, or if it is otherwise not usable to + //! construct `E`. template requires(detail::unexpect_dangles_v) constexpr expected(unexpect_t, Args&&...) = BEMAN_EXPECTED_DELETE_MSG( @@ -275,6 +320,8 @@ class expected { // Deleted catch-all: reference E, argument neither constructible nor a dangling case // (e.g. binding a non-const E& from a const lvalue). + //! \at expected.object.cons + //! \also unexpect-ctor-deleted template requires(std::is_reference_v && !std::is_constructible_v && !detail::unexpect_dangles_v) @@ -286,6 +333,10 @@ class expected { requires(!std::is_reference_v && std::is_constructible_v&, Args...>) constexpr explicit expected(unexpect_t, std::initializer_list il, Args&&... args); + //! \at expected.object.cons + //! \remarks An overload with the same parameter types is defined as + //! deleted when `E` is an lvalue reference type. An initializer list + //! cannot provide the required long-lived error referent. template requires std::is_reference_v constexpr expected(unexpect_t, std::initializer_list, Args&&...) = BEMAN_EXPECTED_DELETE_MSG( @@ -295,6 +346,10 @@ class expected { // [expected.object.dtor] Destructor // ------------------------------------------------------------------------- + //! \at expected.object.dtor + //! \effects None: `val` or `unex` (whichever is active) has a trivial + //! destructor. + //! \remarks This destructor is trivial. constexpr ~expected() requires(std::is_trivially_destructible_v && std::is_trivially_destructible_v) = default; @@ -307,6 +362,10 @@ class expected { // ------------------------------------------------------------------------- // Copy assignment (trivial path) + //! \at expected.object.assign + //! \effects Trivially copies `rhs`'s active member into `*this`. + //! \returns `*this`. + //! \remarks This operator is trivial. constexpr expected& operator=(const expected&) requires(std::is_trivially_copy_constructible_v && std::is_trivially_copy_assignable_v && std::is_trivially_destructible_v && std::is_trivially_copy_constructible_v && @@ -326,6 +385,10 @@ class expected { std::is_trivially_copy_assignable_v && std::is_trivially_destructible_v)); // Move assignment (trivial path) + //! \at expected.object.assign + //! \effects Trivially moves `rhs`'s active member into `*this`. + //! \returns `*this`. + //! \remarks This operator is trivial. constexpr expected& operator=(expected&&) noexcept requires(std::is_trivially_move_constructible_v && std::is_trivially_move_assignable_v && std::is_trivially_destructible_v && std::is_trivially_move_constructible_v && @@ -380,11 +443,18 @@ class expected { constexpr expected& operator=(unexpected&& e); // Deleted for reference E with value G: would rebind E& to unexpected's temporary storage. + //! \at expected.object.assign + //! \group cvt-unexpected-assign-deleted + //! \remarks When `E` is a reference type, an overload taking + //! `unexpected` for a non-reference `G` is defined as deleted: it + //! would rebind `unex` to `unexpected`'s temporary storage. template requires(std::is_reference_v && !std::is_reference_v) constexpr expected& operator=(const unexpected&) = BEMAN_EXPECTED_DELETE_MSG( "expected: cannot assign from unexpected; the value would dangle — use unexpected"); + //! \at expected.object.assign + //! \also cvt-unexpected-assign-deleted template requires(std::is_reference_v && !std::is_reference_v) constexpr expected& operator=(unexpected&&) = BEMAN_EXPECTED_DELETE_MSG( @@ -411,6 +481,8 @@ class expected { std::is_move_constructible_v && std::is_move_constructible_v && (std::is_nothrow_move_constructible_v || std::is_nothrow_move_constructible_v)); + //! \at expected.object.swap + //! \effects Equivalent to `x.swap(y)`. friend constexpr void swap(expected& x, expected& y) noexcept(noexcept(x.swap(y))) requires(std::is_swappable_v && (std::is_reference_v || std::is_swappable_v) && std::is_move_constructible_v && std::is_move_constructible_v && @@ -518,6 +590,14 @@ class expected { // [expected.object.eq] Equality operators (hidden friends) // ------------------------------------------------------------------------- + //! \at expected.object.eq + //! \mandates `!is_void_v` is `true`. The expression `*x == *y` is + //! well-formed and its result is convertible to `bool`. The expression + //! `x.error() == y.error()` is well-formed and its result is + //! convertible to `bool`. + //! \returns If `x.has_value() != y.has_value()`, `false`; otherwise, if + //! `x.has_value()` is `true`, `*x == *y`; otherwise `x.error() == + //! y.error()`. template requires(!std::is_void_v) friend constexpr bool operator==(const expected& x, const expected& y) { @@ -528,29 +608,42 @@ class expected { return x.error() == y.error(); } + //! \at expected.object.eq + //! \mandates `T2` is not a specialization of `expected`. The expression + //! `*x == val` is well-formed and its result is convertible to `bool`. + //! \returns `x.has_value() && static_cast(*x == val)`. template requires(!detail::is_expected_specialization::value) friend constexpr bool operator==(const expected& x, const T2& val) { return x.has_value() && static_cast(*x == val); } + //! \at expected.object.eq + //! \mandates The expression `x.error() == e.error()` is well-formed and + //! its result is convertible to `bool`. + //! \returns `!x.has_value() && static_cast(x.error() == + //! e.error())`. template friend constexpr bool operator==(const expected& x, const unexpected& e) { return !x.has_value() && static_cast(x.error() == e.error()); } private: + //! \expos bool has_val_; union { - T val_; + //! \expos + T val_; + //! \expos unexpected unex_; }; }; -// ============================================================================= -// [expected.object.cons] Out-of-line constructor definitions -// ============================================================================= +// \rSec3[expected.object.cons]{Constructors} +//! \effects Value-initializes `val`. +//! \ensures `has_value()` is `true`. +//! \throws Any exception thrown by the initialization of `val`. template constexpr expected::expected() noexcept(std::is_nothrow_default_constructible_v) requires std::is_default_constructible_v @@ -558,6 +651,16 @@ constexpr expected::expected() noexcept(std::is_nothrow_default_constructi std::construct_at(std::addressof(val_)); } +//! \effects If `rhs.has_value()` is `true`, direct-non-list-initializes +//! `val` with `*rhs`. Otherwise, direct-non-list-initializes `unex` with +//! `rhs.error()`. +//! \ensures `rhs.has_value() == this->has_value()`. +//! \throws Any exception thrown by the initialization of `val` or `unex`. +//! \remarks This constructor is defined as deleted unless +//! `is_copy_constructible_v` is `true` and `is_copy_constructible_v` +//! is `true` or `is_reference_v` is `true`. This constructor is trivial +//! if `is_trivially_copy_constructible_v` is `true` and +//! `is_trivially_copy_constructible_v` is `true`. template constexpr expected::expected(const expected& rhs) noexcept(std::is_nothrow_copy_constructible_v && std::is_nothrow_copy_constructible_v) @@ -570,6 +673,15 @@ constexpr expected::expected(const expected& rhs) noexcept(std::is_nothrow std::construct_at(std::addressof(unex_), rhs.unex_); } +//! \effects If `rhs.has_value()` is `true`, direct-non-list-initializes +//! `val` with `std::move(*rhs)`. Otherwise, direct-non-list-initializes +//! `unex` with `std::move(rhs.error())`. +//! \ensures `rhs.has_value()` is unchanged; `rhs.has_value() == +//! this->has_value()` is `true`. +//! \throws Any exception thrown by the initialization of `val` or `unex`. +//! \remarks This constructor is trivial if +//! `is_trivially_move_constructible_v` is `true` and +//! `is_trivially_move_constructible_v` is `true`. template constexpr expected::expected(expected&& rhs) noexcept(std::is_nothrow_move_constructible_v && std::is_nothrow_move_constructible_v) @@ -582,6 +694,21 @@ constexpr expected::expected(expected&& rhs) noexcept(std::is_nothrow_move std::construct_at(std::addressof(unex_), std::move(rhs.unex_)); } +//! \group cvt-copy-ctor +//! \constraints `is_constructible_v` is `true`; and +//! `is_constructible_v` is `true`; and if `T` is not `bool`, +//! `converts-from-any-cvref>` is `false`; and +//! `is_constructible_v, expected&>` is `false`; and +//! `is_constructible_v, expected>` is `false`; and +//! `is_constructible_v, const expected&>` is `false`; +//! and `is_constructible_v, const expected>` is +//! `false`. +//! \effects If `rhs.has_value()`, direct-non-list-initializes `val` with +//! `*rhs`. Otherwise, direct-non-list-initializes `unex` with +//! `rhs.error()`. +//! \ensures `rhs.has_value()` is unchanged; `rhs.has_value() == +//! this->has_value()` is `true`. +//! \throws Any exception thrown by the initialization of `val` or `unex`. template template requires(!std::is_reference_v && std::is_constructible_v && std::is_constructible_v && @@ -597,6 +724,7 @@ constexpr expected::expected(const expected& rhs) : has_val_(rhs.has std::construct_at(std::addressof(unex_), rhs.error()); } +//! \also cvt-copy-ctor template template requires(!std::is_reference_v && std::is_constructible_v && std::is_constructible_v && @@ -612,6 +740,18 @@ constexpr expected::expected(expected&& rhs) : has_val_(rhs.has_valu std::construct_at(std::addressof(unex_), std::move(rhs).error()); } +//! \group cvt-copy-ctor-ref +//! \constraints `is_reference_v` is `true` and `is_convertible_v` +//! is `true`. +//! \effects If `rhs.has_value()`, direct-non-list-initializes `val` with +//! `*rhs`. Otherwise, direct-non-list-initializes `unex` with +//! `rhs.error()`. +//! \ensures `rhs.has_value()` is unchanged; `rhs.has_value() == +//! this->has_value()` is `true`. +//! \throws Any exception thrown by the initialization of `val` or `unex`. +//! \remarks Unlike the value-`E` overload above, this overload +//! participates in overload resolution only when `E` and `G` are both +//! reference types, so the referenced error object is never copied. template template requires(std::is_reference_v && std::is_reference_v && std::is_constructible_v && @@ -623,6 +763,7 @@ constexpr expected::expected(const expected& rhs) : has_val_(rhs.has std::construct_at(std::addressof(unex_), rhs.error()); } +//! \also cvt-copy-ctor-ref template template requires(std::is_reference_v && std::is_reference_v && std::is_constructible_v && @@ -634,6 +775,15 @@ constexpr expected::expected(expected&& rhs) : has_val_(rhs.has_valu std::construct_at(std::addressof(unex_), rhs.error()); } +//! \constraints `is_same_v, in_place_t>` is `false`; and +//! `is_same_v, unexpect_t>` is `false`; and +//! `is_same_v, expected>` is `false`; and +//! `is_constructible_v` is `true`; and `remove_cvref_t` is not a +//! specialization of `unexpected`; and if `T` is `bool`, +//! `remove_cvref_t` is not a specialization of `expected`. +//! \effects Direct-non-list-initializes `val` with `std::forward(v)`. +//! \ensures `has_value()` is `true`. +//! \throws Any exception thrown by the initialization of `val`. template template requires(!std::is_same_v, std::in_place_t> && @@ -646,6 +796,12 @@ constexpr expected::expected(U&& v) : has_val_(true) { std::construct_at(std::addressof(val_), std::forward(v)); } +//! \group cvt-unexpected-ctor +//! \constraints `is_constructible_v` is `true`. +//! \effects Direct-non-list-initializes `unex` with +//! `std::forward(e.error())`. +//! \ensures `has_value()` is `false`. +//! \throws Any exception thrown by the initialization of `unex`. template template requires(!std::is_reference_v && std::is_constructible_v) @@ -653,6 +809,7 @@ constexpr expected::expected(const unexpected& e) : has_val_(false) { std::construct_at(std::addressof(unex_), e.error()); } +//! \also cvt-unexpected-ctor template template requires(!std::is_reference_v && std::is_constructible_v) @@ -660,6 +817,14 @@ constexpr expected::expected(unexpected&& e) : has_val_(false) { std::construct_at(std::addressof(unex_), std::move(e).error()); } +//! \group cvt-unexpected-ctor-ref +//! \constraints `is_reference_v` is `true`; and `is_constructible_v` is `true`; and `reference_constructs_from_temporary_v` is +//! `false`. +//! \effects Initializes `unex` with `e.error()`. +//! \ensures `has_value()` is `false`. +//! \remarks This constructor never throws: the referent is bound, not +//! copied. template template requires(std::is_reference_v && std::is_reference_v && std::is_constructible_v && @@ -668,6 +833,7 @@ constexpr expected::expected(const unexpected& e) noexcept : has_val_(f std::construct_at(std::addressof(unex_), e.error()); } +//! \also cvt-unexpected-ctor-ref template template requires(std::is_reference_v && std::is_reference_v && std::is_constructible_v && @@ -676,6 +842,11 @@ constexpr expected::expected(unexpected&& e) noexcept : has_val_(false) std::construct_at(std::addressof(unex_), e.error()); } +//! \constraints `is_constructible_v` is `true`. +//! \effects Direct-non-list-initializes `val` with +//! `std::forward(args)...`. +//! \ensures `has_value()` is `true`. +//! \throws Any exception thrown by the initialization of `val`. template template requires std::is_constructible_v @@ -683,6 +854,12 @@ constexpr expected::expected(std::in_place_t, Args&&... args) : has_val_(t std::construct_at(std::addressof(val_), std::forward(args)...); } +//! \constraints `is_constructible_v&, Args...>` is +//! `true`. +//! \effects Direct-non-list-initializes `val` with `il, +//! std::forward(args)...`. +//! \ensures `has_value()` is `true`. +//! \throws Any exception thrown by the initialization of `val`. template template requires std::is_constructible_v&, Args...> @@ -690,6 +867,11 @@ constexpr expected::expected(std::in_place_t, std::initializer_list il, std::construct_at(std::addressof(val_), il, std::forward(args)...); } +//! \constraints `is_constructible_v` is `true`. +//! \effects Direct-non-list-initializes `unex` with +//! `std::forward(args)...`. +//! \ensures `has_value()` is `false`. +//! \throws Any exception thrown by the initialization of `unex`. template template requires(std::is_constructible_v && !detail::unexpect_dangles_v) @@ -697,6 +879,15 @@ constexpr expected::expected(unexpect_t, Args&&... args) : has_val_(false) std::construct_at(std::addressof(unex_), std::in_place, std::forward(args)...); } +//! \constraints `is_reference_v` is `false`, and `is_constructible_v&, Args...>` is `true`. +//! \effects Direct-non-list-initializes `unex` with `il, +//! std::forward(args)...`. +//! \ensures `has_value()` is `false`. +//! \throws Any exception thrown by the initialization of `unex`. +//! \remarks An overload with the same parameter types is defined as +//! deleted when `E` is an lvalue reference type. An initializer list +//! cannot provide the required long-lived error referent. template template requires(!std::is_reference_v && std::is_constructible_v&, Args...>) @@ -704,10 +895,10 @@ constexpr expected::expected(unexpect_t, std::initializer_list il, Args std::construct_at(std::addressof(unex_), std::in_place, il, std::forward(args)...); } -// ============================================================================= -// [expected.object.dtor] Out-of-line destructor -// ============================================================================= +// \rSec3[expected.object.dtor]{Destructor} +//! \effects If `has_value()` is `true`, destroys `val`, otherwise destroys +//! `unex`. template constexpr expected::~expected() requires(!(std::is_trivially_destructible_v && std::is_trivially_destructible_v)) @@ -719,9 +910,30 @@ constexpr expected::~expected() } // ============================================================================= -// [expected.object.assign] Out-of-line assignment definitions -// ============================================================================= - +// \rSec3[expected.object.assign]{Assignment} + +//! \effects If `this->has_value() && rhs.has_value()`, equivalent to `val +//! = *rhs`. Otherwise, if `this->has_value()`, equivalent to +//! `reinit-expected(unex, val, rhs.error())`. Otherwise, if +//! `rhs.has_value()`, equivalent to `reinit-expected(val, unex, *rhs)`. +//! Otherwise, equivalent to `unex = rhs.unex`. Then, if no exception was +//! thrown, equivalent to: `has_val = rhs.has_value(); return *this;` When +//! `E` is an lvalue reference type, each of the cases above that +//! initializes or assigns `unex` rebinds it: `unex` comes to refer to the +//! same object as `rhs`'s error. No previously or subsequently referenced +//! object is assigned through. +//! \returns `*this`. +//! \remarks This operator is defined as deleted unless +//! `is_copy_assignable_v` is `true` and `is_copy_constructible_v` is +//! `true` and `is_copy_assignable_v` is `true` or `is_reference_v` +//! is `true` and `is_copy_constructible_v` is `true` or +//! `is_reference_v` is `true` and `is_nothrow_move_constructible_v +//! || is_nothrow_move_constructible_v` is `true`. This operator is +//! trivial if `is_trivially_copy_constructible_v`, +//! `is_trivially_copy_assignable_v`, `is_trivially_destructible_v`, +//! `is_trivially_copy_constructible_v`, +//! `is_trivially_copy_assignable_v`, and +//! `is_trivially_destructible_v` are all `true`. template constexpr expected& expected::operator=(const expected& rhs) noexcept( std::is_nothrow_copy_constructible_v && std::is_nothrow_copy_assignable_v && @@ -749,6 +961,27 @@ constexpr expected& expected::operator=(const expected& rhs) noexcep return *this; } +//! \effects If `this->has_value() && rhs.has_value()`, equivalent to `val +//! = std::move(*rhs)`. Otherwise, if `this->has_value()`, equivalent to +//! `reinit-expected(unex, val, std::move(rhs.error()))`. Otherwise, if +//! `rhs.has_value()`, equivalent to `reinit-expected(val, unex, +//! std::move(*rhs))`. Otherwise, equivalent to `unex = +//! std::move(rhs.unex)`. Then, if no exception was thrown, equivalent to: +//! `has_val = rhs.has_value(); return *this;` When `E` is an lvalue +//! reference type, each of the cases above that initializes or assigns +//! `unex` rebinds it: `unex` comes to refer to the same object as `rhs`'s +//! error. No previously or subsequently referenced object is assigned +//! through. +//! \returns `*this`. +//! \remarks The exception specification is equivalent to +//! `is_nothrow_move_assignable_v && is_nothrow_move_constructible_v +//! && is_nothrow_move_assignable_v && +//! is_nothrow_move_constructible_v`. This operator is trivial if +//! `is_trivially_move_constructible_v`, +//! `is_trivially_move_assignable_v`, `is_trivially_destructible_v`, +//! `is_trivially_move_constructible_v`, +//! `is_trivially_move_assignable_v`, and +//! `is_trivially_destructible_v` are all `true`. template constexpr expected& expected::operator=(expected&& rhs) noexcept(std::is_nothrow_move_constructible_v && std::is_nothrow_move_assignable_v && @@ -775,6 +1008,16 @@ constexpr expected& expected::operator=(expected&& rhs) noexcept(std return *this; } +//! \constraints `is_same_v>` is `false`; and +//! `remove_cvref_t` is not a specialization of `unexpected`; and +//! `is_constructible_v` is `true`; and `is_assignable_v` is +//! `true`; and `is_nothrow_constructible_v || +//! is_nothrow_move_constructible_v || +//! is_nothrow_move_constructible_v` is `true`. +//! \effects If `has_value()` is `true`, equivalent to: `val = +//! std::forward(v);` Otherwise, equivalent to: `reinit-expected(val, +//! unex, std::forward(v)); has_val = true;` +//! \returns `*this`. template template requires(!std::is_same_v, std::remove_cvref_t> && @@ -792,6 +1035,16 @@ constexpr expected& expected::operator=(U&& v) { return *this; } +//! \group cvt-unexpected-assign +//! \constraints `is_constructible_v` is `true`; and +//! `is_assignable_v` is `true`; and +//! `is_nothrow_constructible_v || +//! is_nothrow_move_constructible_v || +//! is_nothrow_move_constructible_v` is `true`. +//! \effects If `has_value()` is `true`, equivalent to: +//! `reinit-expected(unex, val, e.error()); has_val = false;` Otherwise, +//! equivalent to: `unex = unexpected(e.error());` +//! \returns `*this`. template template requires(!std::is_reference_v && std::is_constructible_v && std::is_assignable_v && @@ -807,6 +1060,7 @@ constexpr expected& expected::operator=(const unexpected& e) { return *this; } +//! \also cvt-unexpected-assign template template requires(!std::is_reference_v && std::is_constructible_v && std::is_assignable_v && @@ -825,6 +1079,16 @@ constexpr expected& expected::operator=(unexpected&& e) { // Rebinding assignment for reference E from reference G. Repoints unex_ (unexpected) to the // external referent via construct_at — NOT `unex_.error() = ...`, which would mutate the old // pointee instead of rebinding. Binding is noexcept; e.error() is the shallow external E&. +//! \group cvt-unexpected-assign-ref +//! \constraints `is_reference_v` is `true`; and `is_constructible_v` is `true`; and `reference_constructs_from_temporary_v` is +//! `false`. +//! \effects Rebinds `unex` to refer to the same object as `e.error()`, +//! destroying `val` first if `has_value()` is `true`. +//! \ensures `has_value()` is `false`. +//! \returns `*this`. +//! \remarks This operator never throws: the referent is bound, not +//! copied. template template requires(std::is_reference_v && std::is_reference_v && std::is_constructible_v && @@ -840,6 +1104,7 @@ constexpr expected& expected::operator=(const unexpected& e) { return *this; } +//! \also cvt-unexpected-assign-ref template template requires(std::is_reference_v && std::is_reference_v && std::is_constructible_v && @@ -859,6 +1124,10 @@ constexpr expected& expected::operator=(unexpected&& e) { // [expected.object.assign] Out-of-line emplace definitions // ============================================================================= +//! \constraints `is_nothrow_constructible_v` is `true`. +//! \effects Equivalent to: `if (has_value()) { destroy_at(addressof(val)); +//! } else { destroy_at(addressof(unex)); has_val = true; } return +//! *construct_at(addressof(val), std::forward(args)...);` template template requires std::is_nothrow_constructible_v @@ -872,6 +1141,12 @@ constexpr T& expected::emplace(Args&&... args) noexcept { return val_; } +//! \constraints `is_nothrow_constructible_v&, +//! Args...>` is `true`. +//! \effects Equivalent to: `if (has_value()) { +//! destroy_at(addressof(val)); } else { destroy_at(addressof(unex)); +//! has_val = true; } return *construct_at(addressof(val), il, +//! std::forward(args)...);` template template requires std::is_nothrow_constructible_v&, Args...> @@ -885,10 +1160,22 @@ constexpr T& expected::emplace(std::initializer_list il, Args&&... args return val_; } -// ============================================================================= -// [expected.object.swap] Out-of-line swap definition -// ============================================================================= - +// \rSec3[expected.object.swap]{Swap} + +//! \effects If `this->has_value()` and `rhs.has_value()`, equivalent to +//! `using std::swap; swap(val, rhs.val);`. If neither `*this` nor `rhs` +//! contains a value, equivalent to `using std::swap; swap(unex, +//! rhs.unex);`. If `rhs.has_value()` is `false` and `this->has_value()` is +//! `true`, exchanges the value and error between `*this` and `rhs` +//! (moving through a temporary so a failed move leaves both objects +//! unchanged), leaving `has_value()` `false` and `rhs.has_value()` `true`. +//! If `rhs.has_value()` is `true` and `this->has_value()` is `false`, +//! equivalent to `rhs.swap(*this)`. +//! \throws Any exception thrown by the expressions in the Effects. +//! \remarks The exception specification is equivalent to +//! `is_nothrow_move_constructible_v && is_nothrow_swappable_v && +//! is_nothrow_move_constructible_v && (is_reference_v || +//! is_nothrow_swappable_v)`. template constexpr void expected::swap(expected& rhs) noexcept(std::is_nothrow_move_constructible_v && std::is_nothrow_swappable_v && @@ -944,9 +1231,11 @@ constexpr void expected::swap(expected& rhs) noexcept(std::is_nothrow_move } // ============================================================================= -// [expected.object.obs] Out-of-line observer definitions -// ============================================================================= +// \rSec3[expected.object.obs]{Observers} +//! \group obs-arrow +//! \hardexpects `has_value()` is `true`. +//! \returns `addressof(val)`. template constexpr const T* expected::operator->() const noexcept { #if defined(BEMAN_EXPECTED_HARDENED) @@ -956,6 +1245,7 @@ constexpr const T* expected::operator->() const noexcept { return std::addressof(val_); } +//! \also obs-arrow template constexpr T* expected::operator->() noexcept { #if defined(BEMAN_EXPECTED_HARDENED) @@ -965,6 +1255,9 @@ constexpr T* expected::operator->() noexcept { return std::addressof(val_); } +//! \group obs-star-lval +//! \hardexpects `has_value()` is `true`. +//! \returns `val`. template constexpr const T& expected::operator*() const& noexcept { #if defined(BEMAN_EXPECTED_HARDENED) @@ -974,6 +1267,7 @@ constexpr const T& expected::operator*() const& noexcept { return val_; } +//! \also obs-star-lval template constexpr T& expected::operator*() & noexcept { #if defined(BEMAN_EXPECTED_HARDENED) @@ -983,6 +1277,9 @@ constexpr T& expected::operator*() & noexcept { return val_; } +//! \group obs-star-rval +//! \hardexpects `has_value()` is `true`. +//! \returns `std::move(val)`. template constexpr const T&& expected::operator*() const&& noexcept { #if defined(BEMAN_EXPECTED_HARDENED) @@ -992,6 +1289,7 @@ constexpr const T&& expected::operator*() const&& noexcept { return std::move(val_); } +//! \also obs-star-rval template constexpr T&& expected::operator*() && noexcept { #if defined(BEMAN_EXPECTED_HARDENED) @@ -1001,16 +1299,24 @@ constexpr T&& expected::operator*() && noexcept { return std::move(val_); } +//! \group obs-bool +//! \returns `has_val`. template constexpr expected::operator bool() const noexcept { return has_val_; } +//! \also obs-bool template constexpr bool expected::has_value() const noexcept { return has_val_; } +//! \group obs-value-lval +//! \mandates `is_copy_constructible_v` is `true`. +//! \returns `val`, if `has_value()` is `true`. +//! \throws `bad_expected_access(as_const(error()))` if `has_value()` is +//! `false`. template constexpr const T& expected::value() const& { static_assert(std::is_copy_constructible_v, "value() requires is_copy_constructible_v"); @@ -1019,6 +1325,7 @@ constexpr const T& expected::value() const& { return val_; } +//! \also obs-value-lval template constexpr T& expected::value() & { static_assert(std::is_copy_constructible_v, "value() requires is_copy_constructible_v"); @@ -1027,6 +1334,12 @@ constexpr T& expected::value() & { return val_; } +//! \group obs-value-rval +//! \mandates `is_copy_constructible_v` is `true` and +//! `is_constructible_v` is `true`. +//! \returns `std::move(val)`, if `has_value()` is `true`. +//! \throws `bad_expected_access(std::move(error()))` if `has_value()` is +//! `false`. template constexpr const T&& expected::value() const&& { if constexpr (std::is_reference_v) { @@ -1041,6 +1354,7 @@ constexpr const T&& expected::value() const&& { return std::move(val_); } +//! \also obs-value-rval template constexpr T&& expected::value() && { if constexpr (std::is_reference_v) { @@ -1055,6 +1369,9 @@ constexpr T&& expected::value() && { return std::move(val_); } +//! \group obs-error-lval +//! \hardexpects `has_value()` is `false`. +//! \returns `unex.error()`. template constexpr const E& expected::error() const& noexcept { #if defined(BEMAN_EXPECTED_HARDENED) @@ -1064,6 +1381,7 @@ constexpr const E& expected::error() const& noexcept { return unex_.error(); } +//! \also obs-error-lval template constexpr E& expected::error() & noexcept { #if defined(BEMAN_EXPECTED_HARDENED) @@ -1073,6 +1391,9 @@ constexpr E& expected::error() & noexcept { return unex_.error(); } +//! \group obs-error-rval +//! \hardexpects `has_value()` is `false`. +//! \returns `std::move(unex).error()`. template constexpr const E&& expected::error() const&& noexcept { #if defined(BEMAN_EXPECTED_HARDENED) @@ -1082,6 +1403,7 @@ constexpr const E&& expected::error() const&& noexcept { return std::move(unex_).error(); } +//! \also obs-error-rval template constexpr E&& expected::error() && noexcept { #if defined(BEMAN_EXPECTED_HARDENED) @@ -1091,6 +1413,7 @@ constexpr E&& expected::error() && noexcept { return std::move(unex_).error(); } +//! \returns `has_value() ? **this : static_cast(std::forward(def))`. template template constexpr T expected::value_or(U&& def) const& { @@ -1101,6 +1424,8 @@ constexpr T expected::value_or(U&& def) const& { return static_cast(std::forward(def)); } +//! \returns `has_value() ? std::move(**this) : +//! static_cast(std::forward(def))`. template template constexpr T expected::value_or(U&& def) && { @@ -1111,6 +1436,10 @@ constexpr T expected::value_or(U&& def) && { return static_cast(std::forward(def)); } +//! \mandates `is_copy_constructible_v` is `true` and +//! `is_convertible_v` is `true`. +//! \returns `std::forward(def)` if `has_value()` is `true`, `error()` +//! otherwise. template template requires(std::is_copy_constructible_v::error_value_type> && @@ -1121,6 +1450,10 @@ constexpr typename expected::error_value_type expected::error_or(G&& return static_cast(std::forward(def)); } +//! \mandates `is_move_constructible_v` is `true` and +//! `is_convertible_v` is `true`. +//! \returns `std::forward(def)` if `has_value()` is `true`, +//! `std::move(error())` otherwise. template template requires(std::is_move_constructible_v::error_value_type> && @@ -1132,9 +1465,16 @@ constexpr typename expected::error_value_type expected::error_or(G&& } // ============================================================================= -// [expected.object.monadic] Out-of-line monadic operation definitions -// ============================================================================= - +// \rSec3[expected.object.monadic]{Monadic operations} + +//! \group monadic-and-then-lval +//! \constraints `is_constructible_v` is `true`. +//! \mandates `remove_cvref_t>` is a +//! specialization of `expected` and its `error_type` is the same type as +//! `E`. +//! \effects Equivalent to: `if (has_value()) return +//! invoke(std::forward(f), val); else return U(unexpect, error());` +//! where `U` is `remove_cvref_t>`. template template requires std::is_constructible_v @@ -1149,6 +1489,16 @@ constexpr auto expected::and_then(F&& f) & { return U(unexpect, unex_.error()); } +//! \group monadic-and-then-rval +//! \constraints `is_constructible_v` is +//! `true`. +//! \mandates `remove_cvref_t>` +//! is a specialization of `expected` and its `error_type` is the same +//! type as `E`. +//! \effects Equivalent to: `if (has_value()) return +//! invoke(std::forward(f), std::move(val)); else return U(unexpect, +//! std::move(error()));` where `U` is `remove_cvref_t>`. template template requires std::is_constructible_v @@ -1163,6 +1513,7 @@ constexpr auto expected::and_then(F&& f) && { return U(unexpect, std::move(unex_).error()); } +//! \also monadic-and-then-lval template template requires std::is_constructible_v @@ -1177,6 +1528,7 @@ constexpr auto expected::and_then(F&& f) const& { return U(unexpect, unex_.error()); } +//! \also monadic-and-then-rval template template requires std::is_constructible_v @@ -1191,6 +1543,14 @@ constexpr auto expected::and_then(F&& f) const&& { return U(unexpect, std::move(unex_).error()); } +//! \group monadic-or-else-lval +//! \constraints `is_constructible_v` is `true`. +//! \mandates `remove_cvref_t>` is a +//! specialization of `expected` and its `value_type` is the same type as +//! `T`. +//! \effects Equivalent to: `if (has_value()) return G(in_place, val); else +//! return invoke(std::forward(f), error());` where `G` is +//! `remove_cvref_t>`. template template requires std::is_constructible_v @@ -1204,6 +1564,16 @@ constexpr auto expected::or_else(F&& f) & { return std::invoke(std::forward(f), unex_.error()); } +//! \group monadic-or-else-rval +//! \constraints `is_constructible_v` is +//! `true`. +//! \mandates `remove_cvref_t>` is a specialization of `expected` and +//! its `value_type` is the same type as `T`. +//! \effects Equivalent to: `if (has_value()) return G(in_place, +//! std::move(val)); else return invoke(std::forward(f), +//! std::move(error()));` where `G` is `remove_cvref_t>`. template template requires std::is_constructible_v @@ -1217,6 +1587,7 @@ constexpr auto expected::or_else(F&& f) && { return std::invoke(std::forward(f), std::move(unex_).error()); } +//! \also monadic-or-else-lval template template requires std::is_constructible_v @@ -1230,6 +1601,7 @@ constexpr auto expected::or_else(F&& f) const& { return std::invoke(std::forward(f), unex_.error()); } +//! \also monadic-or-else-rval template template requires std::is_constructible_v @@ -1243,6 +1615,13 @@ constexpr auto expected::or_else(F&& f) const&& { return std::invoke(std::forward(f), std::move(unex_).error()); } +//! \group monadic-transform-lval +//! \constraints `is_constructible_v` is `true`. +//! \effects Equivalent to: `if (!has_value()) return U(unexpect, +//! error()); else return expected(in_place, +//! invoke(std::forward(f), val));` where `U2` is +//! `remove_cv_t>` and `U` is +//! `expected`. template template requires std::is_constructible_v @@ -1268,6 +1647,14 @@ constexpr auto expected::transform(F&& f) & { } } +//! \group monadic-transform-rval +//! \constraints `is_constructible_v` is +//! `true`. +//! \effects Equivalent to: `if (!has_value()) return +//! U(unexpect, std::move(error())); else return expected(in_place, invoke(std::forward(f), std::move(val)));` where `U2` +//! is `remove_cv_t>` and `U` +//! is `expected`. template template requires std::is_constructible_v @@ -1293,6 +1680,7 @@ constexpr auto expected::transform(F&& f) && { } } +//! \also monadic-transform-lval template template requires std::is_constructible_v @@ -1318,6 +1706,7 @@ constexpr auto expected::transform(F&& f) const& { } } +//! \also monadic-transform-rval template template requires std::is_constructible_v @@ -1343,6 +1732,12 @@ constexpr auto expected::transform(F&& f) const&& { } } +//! \group monadic-transform-error-lval +//! \constraints `is_constructible_v` is `true`. +//! \effects Equivalent to: `if (has_value()) return G(in_place, val); else +//! return expected(unexpect, invoke(std::forward(f), +//! error()));` where `G2` is `remove_cv_t>` and `G` is `expected`. template template requires std::is_constructible_v @@ -1358,6 +1753,14 @@ constexpr auto expected::transform_error(F&& f) & { return expected(unexpect, std::invoke(std::forward(f), unex_.error())); } +//! \group monadic-transform-error-rval +//! \constraints `is_constructible_v` is +//! `true`. +//! \effects Equivalent to: `if (has_value()) return G(in_place, +//! std::move(val)); else return expected(unexpect, +//! invoke(std::forward(f), std::move(error())));` where `G2` is +//! `remove_cv_t>` and +//! `G` is `expected`. template template requires std::is_constructible_v @@ -1373,6 +1776,7 @@ constexpr auto expected::transform_error(F&& f) && { return expected(unexpect, std::invoke(std::forward(f), std::move(unex_).error())); } +//! \also monadic-transform-error-lval template template requires std::is_constructible_v @@ -1388,6 +1792,7 @@ constexpr auto expected::transform_error(F&& f) const& { return expected(unexpect, std::invoke(std::forward(f), unex_.error())); } +//! \also monadic-transform-error-rval template template requires std::is_constructible_v @@ -1403,10 +1808,23 @@ constexpr auto expected::transform_error(F&& f) const&& { return expected(unexpect, std::invoke(std::forward(f), std::move(unex_).error())); } +// \rSec3[expected.object.eq]{Equality operators} + // ============================================================================= // [expected.void] Partial specialization for void value type // ============================================================================= +// \rSec2[expected.void]{Partial specialization of expected for void types} +// \rSec3[expected.void.general]{General} +//! \at expected.void.general +//! \mandates A program that instantiates the definition of `expected` +//! with an `E` that is not a valid template argument for `unexpected` is +//! ill-formed. +//! \remarks Any object of type `expected` either represents a value +//! of type `T`, or contains a value of type `E` nested within it. Member +//! `has_val` indicates whether the `expected` object represents a +//! value of type `T`. When `has_value()` is `false`, the error is +//! `unex.error()`. template class expected { static_assert(!std::is_rvalue_reference_v, "E must not be an rvalue reference"); @@ -1417,6 +1835,7 @@ class expected { "E must not be an unexpected specialization"); private: + //! \expos using error_value_type = std::remove_cv_t>; public: @@ -1436,6 +1855,8 @@ class expected { // 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). + //! \at expected.void.cons + //! \merge constexpr expected(const expected&) = default; constexpr expected(const expected& rhs) noexcept(std::is_nothrow_copy_constructible_v) @@ -1443,6 +1864,8 @@ class expected { // Unconstrained; no explicit noexcept — see the primary template's move // constructor for why. + //! \at expected.void.cons + //! \merge constexpr expected(expected&&) = default; constexpr expected(expected&& rhs) noexcept(std::is_nothrow_move_constructible_v) @@ -1492,11 +1915,20 @@ class expected { // 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. + //! \at expected.void.cons + //! \group void-cvt-unexpected-ctor-deleted + //! \remarks When `E` is a reference type, an overload taking + //! `unexpected` for a non-reference `G` is defined as deleted: the + //! referent would live inside the (possibly temporary) source + //! `unexpected` object, and binding `E&` to it would dangle. Use an + //! `unexpected` holding an external object instead. template requires(std::is_reference_v && !std::is_reference_v) constexpr expected(const unexpected&) = BEMAN_EXPECTED_DELETE_MSG( "expected: cannot construct from unexpected; the value would dangle — use unexpected"); + //! \at expected.void.cons + //! \also void-cvt-unexpected-ctor-deleted template requires(std::is_reference_v && !std::is_reference_v) constexpr expected(unexpected&&) = BEMAN_EXPECTED_DELETE_MSG( @@ -1511,6 +1943,12 @@ class expected { constexpr explicit expected(unexpect_t, Args&&... args); // Deleted: single argument would bind E& to a temporary — dangling prevention + //! \at expected.void.cons + //! \group void-unexpect-ctor-deleted + //! \remarks When `E` is a reference type, an overload with the same + //! parameter types is defined as deleted if the single argument would + //! bind `E&` to a temporary, or if it is otherwise not usable to + //! construct `E`. template requires(detail::unexpect_dangles_v) constexpr expected(unexpect_t, Args&&...) = BEMAN_EXPECTED_DELETE_MSG( @@ -1518,6 +1956,8 @@ class expected { // Deleted catch-all: reference E, argument neither constructible nor a dangling case // (e.g. binding a non-const E& from a const lvalue). + //! \at expected.void.cons + //! \also void-unexpect-ctor-deleted template requires(std::is_reference_v && !std::is_constructible_v && !detail::unexpect_dangles_v) @@ -1529,6 +1969,10 @@ class expected { requires(!std::is_reference_v && std::is_constructible_v&, Args...>) constexpr explicit expected(unexpect_t, std::initializer_list il, Args&&... args); + //! \at expected.void.cons + //! \remarks An overload with the same parameter types is defined as + //! deleted when `E` is an lvalue reference type. An initializer list + //! cannot provide the required long-lived error referent. template requires std::is_reference_v constexpr expected(unexpect_t, std::initializer_list, Args&&...) = BEMAN_EXPECTED_DELETE_MSG( @@ -1553,6 +1997,8 @@ class expected { // [expected.void.dtor] Destructor // ------------------------------------------------------------------------- + //! \at expected.void.dtor + //! \merge constexpr ~expected() requires std::is_trivially_destructible_v = default; @@ -1565,6 +2011,8 @@ class expected { // ------------------------------------------------------------------------- // Copy assignment (trivial path) + //! \at expected.void.assign + //! \merge constexpr expected& operator=(const expected&) requires(std::is_trivially_copy_constructible_v && std::is_trivially_copy_assignable_v && std::is_trivially_destructible_v) @@ -1578,6 +2026,8 @@ class expected { std::is_trivially_destructible_v)); // Move assignment (trivial path) + //! \at expected.void.assign + //! \merge constexpr expected& operator=(expected&&) noexcept requires(std::is_trivially_move_constructible_v && std::is_trivially_move_assignable_v && std::is_trivially_destructible_v) @@ -1611,11 +2061,18 @@ class expected { constexpr expected& operator=(unexpected&& e); // Deleted for reference E with value G: would bind E& to storage inside the temporary unexpected. + //! \at expected.void.assign + //! \group void-cvt-unexpected-assign-deleted + //! \remarks When `E` is a reference type, an overload taking + //! `unexpected` for a non-reference `G` is defined as deleted: it + //! would rebind `unex` to `unexpected`'s temporary storage. template requires(std::is_reference_v && !std::is_reference_v) constexpr expected& operator=(const unexpected&) = BEMAN_EXPECTED_DELETE_MSG( "expected: cannot assign from unexpected; the value would dangle — use unexpected"); + //! \at expected.void.assign + //! \also void-cvt-unexpected-assign-deleted template requires(std::is_reference_v && !std::is_reference_v) constexpr expected& operator=(unexpected&&) = BEMAN_EXPECTED_DELETE_MSG( @@ -1631,6 +2088,8 @@ class expected { (std::is_reference_v || std::is_nothrow_swappable_v)) requires((std::is_reference_v || std::is_swappable_v) && std::is_move_constructible_v); + //! \at expected.void.swap + //! \effects Equivalent to `x.swap(y)`. friend constexpr void swap(expected& x, expected& y) noexcept(noexcept(x.swap(y))) requires((std::is_reference_v || std::is_swappable_v) && std::is_move_constructible_v) { @@ -1667,6 +2126,10 @@ class expected { // 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). + //! \at expected.void.obs + //! \remarks `expected` has no `value_or` member: there is no + //! value to fall back from. This overload exists only to give a clear + //! diagnostic when `E` is a reference type, and is defined as deleted. template requires std::is_reference_v constexpr void value_or(U&&) const = @@ -1725,6 +2188,12 @@ class expected { // [expected.void.eq] Equality operators (hidden friends) // ------------------------------------------------------------------------- + //! \at expected.void.eq + //! \mandates `is_void_v` is `true`. The expression `x.error() == + //! y.error()` is well-formed and its result is convertible to `bool`. + //! \returns If `x.has_value() != y.has_value()`, `false`; otherwise, if + //! `x.has_value()` is `true`, `true`; otherwise `x.error() == + //! y.error()`. template requires std::is_void_v friend constexpr bool operator==(const expected& x, const expected& y) { @@ -1735,28 +2204,44 @@ class expected { return x.error() == y.error(); } + //! \at expected.void.eq + //! \mandates The expression `x.error() == e.error()` is well-formed and + //! its result is convertible to `bool`. + //! \returns `!x.has_value() && static_cast(x.error() == + //! e.error())`. template friend constexpr bool operator==(const expected& x, const unexpected& e) { return !x.has_value() && static_cast(x.error() == e.error()); } private: + //! \expos bool has_val_; union { + //! \expos unexpected unex_; }; }; // ============================================================================= -// [expected.void.cons] Out-of-line constructor definitions -// ============================================================================= +// \rSec3[expected.void.cons]{Constructors} +//! \ensures `has_value()` is `true`. template constexpr expected::expected() noexcept : has_val_(true) {} +//! \ensures `has_value()` is `true`. template constexpr expected::expected(std::in_place_t) noexcept : has_val_(true) {} +//! \effects If `rhs.has_value()` is `false`, direct-non-list-initializes +//! `unex` with `rhs.error()`. +//! \ensures `rhs.has_value() == this->has_value()`. +//! \throws Any exception thrown by the initialization of `unex`. +//! \remarks This constructor is defined as deleted unless +//! `is_copy_constructible_v` is `true` or `is_reference_v` is +//! `true`. This constructor is trivial if +//! `is_trivially_copy_constructible_v` is `true`. template constexpr expected::expected(const expected& rhs) noexcept(std::is_nothrow_copy_constructible_v) requires(std::is_copy_constructible_v && !std::is_trivially_copy_constructible_v) @@ -1765,6 +2250,13 @@ constexpr expected::expected(const expected& rhs) noexcept(std::is_noth std::construct_at(std::addressof(unex_), rhs.unex_); } +//! \effects If `rhs.has_value()` is `false`, direct-non-list-initializes +//! `unex` with `std::move(rhs.error())`. +//! \ensures `rhs.has_value()` is unchanged; `rhs.has_value() == +//! this->has_value()` is `true`. +//! \throws Any exception thrown by the initialization of `unex`. +//! \remarks This constructor is trivial if +//! `is_trivially_move_constructible_v` is `true`. template constexpr expected::expected(expected&& rhs) noexcept(std::is_nothrow_move_constructible_v) requires(std::is_move_constructible_v && !std::is_trivially_move_constructible_v) @@ -1773,6 +2265,18 @@ constexpr expected::expected(expected&& rhs) noexcept(std::is_nothrow_m std::construct_at(std::addressof(unex_), std::move(rhs.unex_)); } +//! \group void-cvt-copy-ctor +//! \constraints `is_void_v` is `true`; and `is_constructible_v` is `true`; and `is_constructible_v, expected&>` is `false`; and `is_constructible_v, expected>` is `false`; and `is_constructible_v, const +//! expected&>` is `false`; and `is_constructible_v, +//! const expected>` is `false`. +//! \effects If `rhs.has_value()` is `false`, direct-non-list-initializes +//! `unex` with `rhs.error()`. +//! \ensures `rhs.has_value()` is unchanged; `rhs.has_value() == +//! this->has_value()` is `true`. +//! \throws Any exception thrown by the initialization of `unex`. template template requires(std::is_void_v && !std::is_reference_v && !std::is_same_v && @@ -1785,6 +2289,7 @@ constexpr expected::expected(const expected& rhs) : has_val_(rhs. std::construct_at(std::addressof(unex_), rhs.error()); } +//! \also void-cvt-copy-ctor template template requires(std::is_void_v && !std::is_reference_v && !std::is_same_v && std::is_constructible_v && @@ -1797,6 +2302,11 @@ constexpr expected::expected(expected&& rhs) : has_val_(rhs.has_v std::construct_at(std::addressof(unex_), std::move(rhs).error()); } +//! \group void-cvt-unexpected-ctor +//! \constraints `is_constructible_v` is `true`. +//! \effects Direct-non-list-initializes `unex` with `e.error()`. +//! \ensures `has_value()` is `false`. +//! \throws Any exception thrown by the initialization of `unex`. template template requires(!std::is_reference_v && std::is_constructible_v) @@ -1804,6 +2314,7 @@ constexpr expected::expected(const unexpected& e) : has_val_(false) std::construct_at(std::addressof(unex_), e.error()); } +//! \also void-cvt-unexpected-ctor template template requires(!std::is_reference_v && std::is_constructible_v) @@ -1811,6 +2322,14 @@ constexpr expected::expected(unexpected&& e) : has_val_(false) { std::construct_at(std::addressof(unex_), std::move(e).error()); } +//! \group void-cvt-unexpected-ctor-ref +//! \constraints `is_reference_v` is `true`; and `is_constructible_v` is `true`; and `reference_constructs_from_temporary_v` is +//! `false`. +//! \effects Initializes `unex` with `e.error()`. +//! \ensures `has_value()` is `false`. +//! \remarks This constructor never throws: the referent is bound, not +//! copied. template template requires(std::is_reference_v && std::is_reference_v && std::is_constructible_v && @@ -1819,6 +2338,7 @@ constexpr expected::expected(const unexpected& e) noexcept : has_val std::construct_at(std::addressof(unex_), e.error()); } +//! \also void-cvt-unexpected-ctor-ref template template requires(std::is_reference_v && std::is_reference_v && std::is_constructible_v && @@ -1827,6 +2347,11 @@ constexpr expected::expected(unexpected&& e) noexcept : has_val_(fal std::construct_at(std::addressof(unex_), e.error()); } +//! \constraints `is_constructible_v` is `true`. +//! \effects Direct-non-list-initializes `unex` with +//! `std::forward(args)...`. +//! \ensures `has_value()` is `false`. +//! \throws Any exception thrown by the initialization of `unex`. template template requires(std::is_constructible_v && !detail::unexpect_dangles_v) @@ -1834,6 +2359,15 @@ constexpr expected::expected(unexpect_t, Args&&... args) : has_val_(fal std::construct_at(std::addressof(unex_), std::in_place, std::forward(args)...); } +//! \constraints `is_reference_v` is `false`, and `is_constructible_v&, Args...>` is `true`. +//! \effects Direct-non-list-initializes `unex` with `il, +//! std::forward(args)...`. +//! \ensures `has_value()` is `false`. +//! \throws Any exception thrown by the initialization of `unex`. +//! \remarks An overload with the same parameter types is defined as +//! deleted when `E` is an lvalue reference type. An initializer list +//! cannot provide the required long-lived error referent. template template requires(!std::is_reference_v && std::is_constructible_v&, Args...>) @@ -1841,6 +2375,17 @@ constexpr expected::expected(unexpect_t, std::initializer_list il, A std::construct_at(std::addressof(unex_), std::in_place, il, std::forward(args)...); } +//! \group void-cvt-copy-ctor-ref +//! \constraints `is_convertible_v` is `true`; and +//! `reference_constructs_from_temporary_v` is `false`. +//! \effects If `rhs.has_value()` is `false`, direct-non-list-initializes +//! `unex` with `rhs.error()`. +//! \ensures `rhs.has_value()` is unchanged; `rhs.has_value() == +//! this->has_value()` is `true`. +//! \remarks This constructor never throws: the referent is bound, not +//! copied. It participates in overload resolution only when `E` is a +//! reference type, mirroring the `unexpected` reference-`E` path +//! above. template template requires(std::is_reference_v && std::is_convertible_v && @@ -1850,6 +2395,7 @@ constexpr expected::expected(const expected& rhs) : has_val_( std::construct_at(std::addressof(unex_), rhs.error()); } +//! \also void-cvt-copy-ctor-ref template template requires(std::is_reference_v && std::is_convertible_v && @@ -1860,9 +2406,11 @@ constexpr expected::expected(expected&& rhs) : has_val_(rhs.h } // ============================================================================= -// [expected.void.dtor] Out-of-line destructor -// ============================================================================= +// \rSec3[expected.void.dtor]{Destructor} +//! \effects If `has_value()` is `false`, destroys `unex`. +//! \remarks If `is_trivially_destructible_v` is `true`, then this +//! destructor is a trivial destructor. template constexpr expected::~expected() requires(!std::is_trivially_destructible_v) @@ -1872,9 +2420,21 @@ constexpr expected::~expected() } // ============================================================================= -// [expected.void.assign] Out-of-line assignment definitions -// ============================================================================= - +// \rSec3[expected.void.assign]{Assignment} + +//! \effects If `this->has_value() && rhs.has_value()`, no effects. +//! Otherwise, if `this->has_value()`, equivalent to: +//! `construct_at(addressof(unex), rhs.unex); has_val = false;` +//! Otherwise, if `rhs.has_value()`, destroys `unex` and sets `has_val` to +//! `true`. Otherwise, equivalent to `unex = rhs.unex`. +//! \returns `*this`. +//! \remarks This operator is defined as deleted unless +//! `is_copy_assignable_v` is `true` or `is_reference_v` is `true` +//! and `is_copy_constructible_v` is `true` or `is_reference_v` is +//! `true`. This operator is trivial if +//! `is_trivially_copy_constructible_v`, +//! `is_trivially_copy_assignable_v`, and +//! `is_trivially_destructible_v` are all `true`. template constexpr expected& expected::operator=(const expected& rhs) noexcept(std::is_nothrow_copy_constructible_v && @@ -1897,6 +2457,18 @@ expected::operator=(const expected& rhs) noexcept(std::is_nothrow_copy_ return *this; } +//! \effects If `this->has_value() && rhs.has_value()`, no effects. +//! Otherwise, if `this->has_value()`, equivalent to: +//! `construct_at(addressof(unex), std::move(rhs.unex)); has_val = false;` +//! Otherwise, if `rhs.has_value()`, destroys `unex` and sets `has_val` to +//! `true`. Otherwise, equivalent to `unex = std::move(rhs.unex)`. +//! \returns `*this`. +//! \remarks The exception specification is equivalent to +//! `is_nothrow_move_constructible_v && +//! is_nothrow_move_assignable_v`. This operator is trivial if +//! `is_trivially_move_constructible_v`, +//! `is_trivially_move_assignable_v`, and +//! `is_trivially_destructible_v` are all `true`. template constexpr expected& expected::operator=(expected&& rhs) noexcept(std::is_nothrow_move_constructible_v && @@ -1919,6 +2491,13 @@ expected::operator=(expected&& rhs) noexcept(std::is_nothrow_move_const return *this; } +//! \group void-cvt-unexpected-assign +//! \constraints `is_constructible_v` is `true` and +//! `is_assignable_v` is `true`. +//! \effects If `has_value()` is `true`, equivalent to: +//! `construct_at(addressof(unex), e.error()); has_val = false;` +//! Otherwise, equivalent to: `unex = unexpected(e.error());` +//! \returns `*this`. template template requires(!std::is_reference_v && std::is_constructible_v && std::is_assignable_v) @@ -1932,6 +2511,7 @@ constexpr expected& expected::operator=(const unexpected& e return *this; } +//! \also void-cvt-unexpected-assign template template requires(!std::is_reference_v && std::is_constructible_v && std::is_assignable_v) @@ -1947,6 +2527,15 @@ constexpr expected& expected::operator=(unexpected&& e) { // Rebinding assignment for reference E from reference G. No value member to destroy; repoint unex_ // via construct_at (not `unex_.error() = ...`, which would mutate the old pointee). +//! \group void-cvt-unexpected-assign-ref +//! \constraints `is_reference_v` is `true`; and `is_constructible_v` is `true`; and `reference_constructs_from_temporary_v` is +//! `false`. +//! \effects Rebinds `unex` to refer to the same object as `e.error()`. +//! \ensures `has_value()` is `false`. +//! \returns `*this`. +//! \remarks This operator never throws: the referent is bound, not +//! copied. template template requires(std::is_reference_v && std::is_reference_v && std::is_constructible_v && @@ -1957,6 +2546,7 @@ constexpr expected& expected::operator=(const unexpected& e return *this; } +//! \also void-cvt-unexpected-assign-ref template template requires(std::is_reference_v && std::is_reference_v && std::is_constructible_v && @@ -1967,6 +2557,8 @@ constexpr expected& expected::operator=(unexpected&& e) { return *this; } +//! \effects If `has_value()` is `false`, destroys `unex` and sets +//! `has_val` to `true`. template constexpr void expected::emplace() noexcept { if (!has_val_) { @@ -1976,9 +2568,19 @@ constexpr void expected::emplace() noexcept { } // ============================================================================= -// [expected.void.swap] Out-of-line swap definition -// ============================================================================= - +// \rSec3[expected.void.swap]{Swap} + +//! \effects If `this->has_value()` and `rhs.has_value()`, no effects. If +//! neither `*this` nor `rhs` contains a value, equivalent to `using +//! std::swap; swap(unex, rhs.unex);`. If `rhs.has_value()` is `false` +//! and `this->has_value()` is `true`, initializes `rhs.unex` from +//! `std::move(unex)`, destroys `unex`, and leaves `has_value()` `false` +//! and `rhs.has_value()` `true`. If `rhs.has_value()` is `true` and +//! `this->has_value()` is `false`, equivalent to `rhs.swap(*this)`. +//! \throws Any exception thrown by the expressions in the Effects. +//! \remarks The exception specification is equivalent to +//! `is_nothrow_move_constructible_v && (is_reference_v || +//! is_nothrow_swappable_v)`. template constexpr void expected::swap(expected& rhs) noexcept(std::is_nothrow_move_constructible_v && (std::is_reference_v || @@ -2001,19 +2603,22 @@ constexpr void expected::swap(expected& rhs) noexcept(std::is_nothrow_m } // ============================================================================= -// [expected.void.obs] Out-of-line observer definitions -// ============================================================================= +// \rSec3[expected.void.obs]{Observers} +//! \group void-obs-bool +//! \returns `has_val`. template constexpr expected::operator bool() const noexcept { return has_val_; } +//! \also void-obs-bool template constexpr bool expected::has_value() const noexcept { return has_val_; } +//! \hardexpects `has_value()` is `true`. template constexpr void expected::operator*() const noexcept { #if defined(BEMAN_EXPECTED_HARDENED) @@ -2022,6 +2627,8 @@ constexpr void expected::operator*() const noexcept { #endif } +//! \mandates `is_copy_constructible_v` is `true`. +//! \throws `bad_expected_access(error())` if `has_value()` is `false`. template constexpr void expected::value() const& { static_assert(std::is_copy_constructible_v, "value() requires E to be copy constructible"); @@ -2029,6 +2636,10 @@ constexpr void expected::value() const& { throw bad_expected_access(unex_.error()); } +//! \mandates `is_copy_constructible_v` is `true` and +//! `is_move_constructible_v` is `true`. +//! \throws `bad_expected_access(std::move(error()))` if `has_value()` is +//! `false`. template constexpr void expected::value() && { static_assert(std::is_copy_constructible_v && std::is_move_constructible_v, @@ -2037,6 +2648,9 @@ constexpr void expected::value() && { throw bad_expected_access(std::move(unex_).error()); } +//! \group void-obs-error-lval +//! \hardexpects `has_value()` is `false`. +//! \returns `unex.error()`. template constexpr const E& expected::error() const& noexcept { #if defined(BEMAN_EXPECTED_HARDENED) @@ -2046,6 +2660,7 @@ constexpr const E& expected::error() const& noexcept { return unex_.error(); } +//! \also void-obs-error-lval template constexpr E& expected::error() & noexcept { #if defined(BEMAN_EXPECTED_HARDENED) @@ -2055,6 +2670,9 @@ constexpr E& expected::error() & noexcept { return unex_.error(); } +//! \group void-obs-error-rval +//! \hardexpects `has_value()` is `false`. +//! \returns `std::move(unex).error()`. template constexpr const E&& expected::error() const&& noexcept { #if defined(BEMAN_EXPECTED_HARDENED) @@ -2064,6 +2682,7 @@ constexpr const E&& expected::error() const&& noexcept { return std::move(unex_).error(); } +//! \also void-obs-error-rval template constexpr E&& expected::error() && noexcept { #if defined(BEMAN_EXPECTED_HARDENED) @@ -2073,6 +2692,10 @@ constexpr E&& expected::error() && noexcept { return std::move(unex_).error(); } +//! \mandates `is_copy_constructible_v` is `true` and +//! `is_convertible_v` is `true`. +//! \returns `std::forward(def)` if `has_value()` is `true`, `error()` +//! otherwise. template template requires(std::is_copy_constructible_v>> && @@ -2083,6 +2706,10 @@ constexpr typename expected::error_value_type expected::error_ return static_cast(std::forward(def)); } +//! \mandates `is_move_constructible_v` is `true` and +//! `is_convertible_v` is `true`. +//! \returns `std::forward(def)` if `has_value()` is `true`, +//! `std::move(error())` otherwise. template template requires(std::is_move_constructible_v>> && @@ -2093,10 +2720,15 @@ constexpr typename expected::error_value_type expected::error_ return static_cast(std::forward(def)); } -// ============================================================================= -// [expected.void.monadic] Out-of-line monadic operation definitions -// ============================================================================= +// \rSec3[expected.void.monadic]{Monadic operations} +//! \group void-monadic-and-then-lval +//! \constraints `is_constructible_v` is `true`. +//! \mandates `remove_cvref_t>` is a specialization of +//! `expected` and its `error_type` is the same type as `E`. +//! \effects Equivalent to: `if (has_value()) return +//! invoke(std::forward(f)); else return U(unexpect, error());` where +//! `U` is `remove_cvref_t>`. template template requires std::is_constructible_v @@ -2111,6 +2743,14 @@ constexpr auto expected::and_then(F&& f) & { return U(unexpect, unex_.error()); } +//! \group void-monadic-and-then-rval +//! \constraints `is_constructible_v` is +//! `true`. +//! \mandates `remove_cvref_t>` is a specialization of +//! `expected` and its `error_type` is the same type as `E`. +//! \effects Equivalent to: `if (has_value()) return +//! invoke(std::forward(f)); else return U(unexpect, +//! std::move(error()));` where `U` is `remove_cvref_t>`. template template requires std::is_constructible_v @@ -2125,6 +2765,7 @@ constexpr auto expected::and_then(F&& f) && { return U(unexpect, std::move(unex_).error()); } +//! \also void-monadic-and-then-lval template template requires std::is_constructible_v @@ -2139,6 +2780,7 @@ constexpr auto expected::and_then(F&& f) const& { return U(unexpect, unex_.error()); } +//! \also void-monadic-and-then-rval template template requires std::is_constructible_v @@ -2153,6 +2795,13 @@ constexpr auto expected::and_then(F&& f) const&& { return U(unexpect, std::move(unex_).error()); } +//! \group void-monadic-or-else-lval +//! \mandates `remove_cvref_t>` is a +//! specialization of `expected` and its `value_type` is the same type as +//! `T`. +//! \effects Equivalent to: `if (has_value()) return G(); else return +//! invoke(std::forward(f), error());` where `G` is +//! `remove_cvref_t>`. template template constexpr auto expected::or_else(F&& f) & { @@ -2165,6 +2814,13 @@ constexpr auto expected::or_else(F&& f) & { return std::invoke(std::forward(f), unex_.error()); } +//! \group void-monadic-or-else-rval +//! \mandates `remove_cvref_t>` is a specialization of `expected` and +//! its `value_type` is the same type as `T`. +//! \effects Equivalent to: `if (has_value()) return G(); else return +//! invoke(std::forward(f), std::move(error()));` where `G` is +//! `remove_cvref_t>`. template template constexpr auto expected::or_else(F&& f) && { @@ -2177,6 +2833,7 @@ constexpr auto expected::or_else(F&& f) && { return std::invoke(std::forward(f), std::move(unex_).error()); } +//! \also void-monadic-or-else-lval template template constexpr auto expected::or_else(F&& f) const& { @@ -2189,6 +2846,7 @@ constexpr auto expected::or_else(F&& f) const& { return std::invoke(std::forward(f), unex_.error()); } +//! \also void-monadic-or-else-rval template template constexpr auto expected::or_else(F&& f) const&& { @@ -2201,6 +2859,16 @@ constexpr auto expected::or_else(F&& f) const&& { return std::invoke(std::forward(f), std::move(unex_).error()); } +//! \group void-monadic-transform-lval +//! \constraints `is_constructible_v` is `true`. +//! \mandates `U` is a valid value type for `expected`, where `U` is +//! `remove_cv_t>`. +//! \effects If `has_value()` is `false`, returns `expected(unexpect, +//! error())`. Otherwise, if `is_void_v` is `false`, returns an +//! `expected` object whose `has_val` member is `true` and `val` +//! member is direct-non-list-initialized with `invoke(std::forward(f))`. +//! Otherwise, evaluates `invoke(std::forward(f))` and then returns +//! `expected()`. template template requires std::is_constructible_v @@ -2226,6 +2894,17 @@ constexpr auto expected::transform(F&& f) & { } } +//! \group void-monadic-transform-rval +//! \constraints `is_constructible_v` is +//! `true`. +//! \mandates `U` is a valid value type for `expected`, where `U` is +//! `remove_cv_t>`. +//! \effects If `has_value()` is `false`, returns `expected(unexpect, +//! std::move(error()))`. Otherwise, if `is_void_v` is `false`, returns +//! an `expected` object whose `has_val` member is `true` and `val` +//! member is direct-non-list-initialized with `invoke(std::forward(f))`. +//! Otherwise, evaluates `invoke(std::forward(f))` and then returns +//! `expected()`. template template requires std::is_constructible_v @@ -2251,6 +2930,7 @@ constexpr auto expected::transform(F&& f) && { } } +//! \also void-monadic-transform-lval template template requires std::is_constructible_v @@ -2276,6 +2956,7 @@ constexpr auto expected::transform(F&& f) const& { } } +//! \also void-monadic-transform-rval template template requires std::is_constructible_v @@ -2301,6 +2982,15 @@ constexpr auto expected::transform(F&& f) const&& { } } +//! \group void-monadic-transform-error-lval +//! \mandates `G` is a valid template argument for `unexpected` and the +//! declaration `G g(invoke(std::forward(f), error()));` is +//! well-formed, where `G` is `remove_cv_t>`. +//! \returns If `has_value()` is `true`, `expected()`; otherwise, an +//! `expected` object whose `has_val` member is `false` and `unex` +//! member is direct-non-list-initialized with `invoke(std::forward(f), +//! error())`. template template constexpr auto expected::transform_error(F&& f) & { @@ -2315,6 +3005,15 @@ constexpr auto expected::transform_error(F&& f) & { return expected(unexpect, std::invoke(std::forward(f), unex_.error())); } +//! \group void-monadic-transform-error-rval +//! \mandates `G` is a valid template argument for `unexpected` and the +//! declaration `G g(invoke(std::forward(f), std::move(error())));` is +//! well-formed, where `G` is `remove_cv_t>`. +//! \returns If `has_value()` is `true`, `expected()`; otherwise, an +//! `expected` object whose `has_val` member is `false` and `unex` +//! member is direct-non-list-initialized with `invoke(std::forward(f), +//! std::move(error()))`. template template constexpr auto expected::transform_error(F&& f) && { @@ -2329,6 +3028,7 @@ constexpr auto expected::transform_error(F&& f) && { return expected(unexpect, std::invoke(std::forward(f), std::move(unex_).error())); } +//! \also void-monadic-transform-error-lval template template constexpr auto expected::transform_error(F&& f) const& { @@ -2343,6 +3043,7 @@ constexpr auto expected::transform_error(F&& f) const& { return expected(unexpect, std::invoke(std::forward(f), unex_.error())); } +//! \also void-monadic-transform-error-rval template template constexpr auto expected::transform_error(F&& f) const&& { @@ -2357,11 +3058,24 @@ constexpr auto expected::transform_error(F&& f) const&& { return expected(unexpect, std::invoke(std::forward(f), std::move(unex_).error())); } +// \rSec3[expected.void.eq]{Equality operators} + // ============================================================================= // Partial specialization: expected — reference value type // (E may be an object type or an lvalue reference to one) // ============================================================================= +// \rSec2[expected.ref]{Partial specialization of expected for reference types} +// \rSec3[expected.ref.general]{General} +//! \at expected.ref.general +//! \mandates A program that instantiates the definition of `expected` with an `E` that is not a valid template argument for `unexpected` +//! is ill-formed. `T` shall be an object type that is not an array type. +//! \remarks An object of type `expected` either represents a +//! reference to an object of type `T`, or holds an error. Member `has_val` +//! indicates whether the object represents a reference. When it represents +//! a reference, member `val` points to the referenced object, which is not +//! owned by the `expected` object. Otherwise, the error is `unex.error()`. template class expected { static_assert(!std::is_array_v, "T must not be an array type"); @@ -2377,6 +3091,7 @@ class expected { static_assert(std::is_reference_v || std::is_same_v, E>, "E must not be cv-qualified"); private: + //! \expos using error_value_type = std::remove_cv_t>; public: @@ -2391,30 +3106,73 @@ class expected { // Constructors // ------------------------------------------------------------------------- + //! \at expected.ref.cons + //! \remarks `expected` has no default constructor: a reference + //! cannot be null, so there is no empty state to default-construct + //! into. 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. + //! \at expected.ref.cons + //! \effects If `rhs.has_value()` is `true`, initializes `val` with + //! `rhs.val`, so that `*this` and `rhs` refer to the same object; + //! otherwise, initializes `unex` with `rhs.unex`. + //! \ensures `rhs.has_value() == this->has_value()`. + //! \remarks This constructor is trivial. constexpr expected(const expected&) = default; // Copy constructor (non-trivial path) + //! \at expected.ref.cons + //! \effects If `rhs.has_value()` is `true`, initializes `val` with + //! `rhs.val`, so that `*this` and `rhs` refer to the same object; + //! otherwise, initializes `unex` with `rhs.unex`. + //! \ensures `rhs.has_value() == this->has_value()`. + //! \remarks This constructor is defined as deleted unless + //! `is_copy_constructible_v` is `true`. constexpr expected(const expected& rhs) noexcept(std::is_nothrow_copy_constructible_v) requires(std::is_copy_constructible_v && !std::is_trivially_copy_constructible_v); // Move constructor (trivial path). Unconstrained; no explicit noexcept. + //! \at expected.ref.cons + //! \effects If `rhs.has_value()` is `true`, initializes `val` with + //! `rhs.val`, so that `*this` and `rhs` refer to the same object; + //! otherwise, initializes `unex` with `std::move(rhs.unex)`. + //! \ensures `rhs.has_value() == this->has_value()`. + //! \remarks This constructor is trivial. constexpr expected(expected&&) = default; // Move constructor (non-trivial path) + //! \at expected.ref.cons + //! \effects If `rhs.has_value()` is `true`, initializes `val` with + //! `rhs.val`, so that `*this` and `rhs` refer to the same object; + //! otherwise, initializes `unex` with `std::move(rhs.unex)`. + //! \ensures `rhs.has_value() == this->has_value()`. + //! \remarks The exception specification is equivalent to + //! `is_nothrow_move_constructible_v`. This constructor is defined as + //! deleted unless `is_move_constructible_v` is `true`. constexpr expected(expected&& rhs) noexcept(std::is_nothrow_move_constructible_v) requires(std::is_move_constructible_v && !std::is_trivially_move_constructible_v); // Deleted: no in-place value constructor — T& cannot be constructed in-place + //! \at expected.ref.cons + //! \remarks `expected` has no in-place value constructor: `T&` + //! cannot be constructed in-place. Pass a `U` convertible to `T&` + //! instead. template constexpr expected(std::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& + //! \at expected.ref.cons + //! \constraints `remove_cvref_t` is not `in_place_t`, `expected`, or + //! a specialization of `unexpected`; `is_constructible_v` is + //! `true`; and `reference_constructs_from_temporary_v` is + //! `false`. + //! \effects Let `r` be the lvalue result of `T& r = + //! std::forward(u);`. Initializes `val` with `addressof(r)`. + //! \ensures `has_value()` is `true`. template requires(!std::is_same_v, std::in_place_t> && !std::is_same_v, expected> && @@ -2427,6 +3185,10 @@ class expected { } // Deleted: binding a temporary to T& creates a dangling reference + //! \at expected.ref.cons + //! \remarks A constructor for which + //! `reference_constructs_from_temporary_v` is `true` — one that + //! would bind `T&` to a temporary — is defined as deleted. template requires(detail::reference_constructs_from_temporary_v) constexpr expected(U&&) = BEMAN_EXPECTED_DELETE_MSG( @@ -2483,11 +3245,20 @@ class expected { // 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. + //! \at expected.ref.cons + //! \group ref-cvt-unexpected-ctor-deleted + //! \remarks When `E` is a reference type, an overload taking + //! `unexpected` for a non-reference `G` is defined as deleted: the + //! referent would live inside the (possibly temporary) source + //! `unexpected` object, and binding `E&` to it would dangle. Use an + //! `unexpected` holding an external object instead. template requires(std::is_reference_v && !std::is_reference_v) constexpr expected(const unexpected&) = BEMAN_EXPECTED_DELETE_MSG( "expected: cannot construct from unexpected; the value would dangle — use unexpected"); + //! \at expected.ref.cons + //! \also ref-cvt-unexpected-ctor-deleted template requires(std::is_reference_v && !std::is_reference_v) constexpr expected(unexpected&&) = BEMAN_EXPECTED_DELETE_MSG( @@ -2499,12 +3270,20 @@ class expected { constexpr explicit expected(unexpect_t, Args&&... args); // Deleted: single argument would bind E& to a temporary — dangling prevention + //! \at expected.ref.cons + //! \group ref-unexpect-ctor-deleted + //! \remarks When `E` is a reference type, an overload with the same + //! parameter types is defined as deleted if the single argument would + //! bind `E&` to a temporary, or if it is otherwise not usable to + //! construct `E`. template requires(detail::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 + //! \at expected.ref.cons + //! \also ref-unexpect-ctor-deleted template requires(std::is_reference_v && !std::is_constructible_v && !detail::unexpect_dangles_v) @@ -2516,6 +3295,10 @@ class expected { requires(!std::is_reference_v && std::is_constructible_v&, Args...>) constexpr explicit expected(unexpect_t, std::initializer_list il, Args&&... args); + //! \at expected.ref.cons + //! \remarks An overload with the same parameter types is defined as + //! deleted when `E` is an lvalue reference type. An initializer list + //! cannot provide the required long-lived error referent. template requires std::is_reference_v constexpr expected(unexpect_t, std::initializer_list, Args&&...) = BEMAN_EXPECTED_DELETE_MSG( @@ -2525,10 +3308,18 @@ class expected { // Destructor // ------------------------------------------------------------------------- + //! \at expected.ref.dtor + //! \effects None: `*this` never owns the object it refers to; `T` is + //! never destroyed. + //! \remarks This destructor is trivial. constexpr ~expected() requires std::is_trivially_destructible_v = default; + //! \at expected.ref.dtor + //! \effects If `has_value()` is `false`, destroys `unex`. `T` is not + //! destroyed; `*this` never owns the object it refers to. + //! \remarks This destructor is trivial if `E` is trivially destructible. constexpr ~expected() requires(!std::is_trivially_destructible_v); @@ -2537,12 +3328,34 @@ class expected { // ------------------------------------------------------------------------- // Copy assignment (trivial path) + //! \at expected.ref.assign + //! \effects If `rhs.has_value()` is `true`: if `has_value()` is `true`, + //! assigns `rhs.val` to `val`; otherwise destroys `unex` and + //! initializes `val` with `rhs.val`. If `rhs.has_value()` is `false`, + //! the error of `rhs` is assigned to or used to initialize `unex`, as + //! for the primary template. In every case `*this` comes to refer to + //! the object `rhs` refers to, or to hold the error of `rhs`. + //! \returns `*this`. + //! \remarks Assignment rebinds: assigning to an `expected` that + //! holds a value changes which object it refers to. It never assigns + //! through to the referent. This operator is trivial. constexpr expected& operator=(const expected&) requires(std::is_trivially_copy_constructible_v && std::is_trivially_copy_assignable_v && std::is_trivially_destructible_v) = default; // Copy assignment (non-trivial path) + //! \at expected.ref.assign + //! \effects If `rhs.has_value()` is `true`: if `has_value()` is `true`, + //! assigns `rhs.val` to `val`; otherwise destroys `unex` and + //! initializes `val` with `rhs.val`. If `rhs.has_value()` is `false`, + //! the error of `rhs` is assigned to or used to initialize `unex`, as + //! for the primary template. In every case `*this` comes to refer to + //! the object `rhs` refers to, or to hold the error of `rhs`. + //! \returns `*this`. + //! \remarks This operator is defined as deleted unless + //! `is_copy_assignable_v` is `true` and `is_copy_constructible_v` + //! is `true`. constexpr expected& operator=(const expected& rhs) noexcept(std::is_nothrow_copy_constructible_v && std::is_nothrow_copy_assignable_v) requires((std::is_reference_v || (std::is_copy_constructible_v && std::is_copy_assignable_v)) && @@ -2550,12 +3363,32 @@ class expected { std::is_trivially_destructible_v)); // Move assignment (trivial path) + //! \at expected.ref.assign + //! \effects If `rhs.has_value()` is `true`: if `has_value()` is `true`, + //! assigns `rhs.val` to `val`; otherwise destroys `unex` and + //! initializes `val` with `rhs.val`. If `rhs.has_value()` is `false`, + //! the error of `rhs` is assigned to or used to initialize `unex`, as + //! for the primary template. + //! \returns `*this`. + //! \remarks This operator is trivial. constexpr expected& operator=(expected&&) noexcept requires(std::is_trivially_move_constructible_v && std::is_trivially_move_assignable_v && std::is_trivially_destructible_v) = default; // Move assignment (non-trivial path) + //! \at expected.ref.assign + //! \effects If `rhs.has_value()` is `true`: if `has_value()` is `true`, + //! assigns `rhs.val` to `val`; otherwise destroys `unex` and + //! initializes `val` with `rhs.val`. If `rhs.has_value()` is `false`, + //! the error of `rhs` is assigned to or used to initialize `unex`, as + //! for the primary template. + //! \returns `*this`. + //! \remarks The exception specification is equivalent to + //! `is_nothrow_move_constructible_v && + //! is_nothrow_move_assignable_v`. This operator is defined as + //! deleted unless `is_move_assignable_v` is `true` and + //! `is_move_constructible_v` is `true`. constexpr expected& operator=(expected&& rhs) noexcept(std::is_nothrow_move_constructible_v && std::is_nothrow_move_assignable_v) requires((std::is_reference_v || (std::is_move_constructible_v && std::is_move_assignable_v)) && @@ -2563,6 +3396,17 @@ class expected { std::is_trivially_destructible_v)); // Rebind reference from lvalue + //! \at expected.ref.assign + //! \constraints `remove_cvref_t` is neither `expected` nor a + //! specialization of `unexpected`, `is_constructible_v` is + //! `true`, and `reference_constructs_from_temporary_v` is + //! `false`. + //! \effects Let `r` be the lvalue result of `T& r = + //! std::forward(u);`. If `has_value()` is `true`, assigns + //! `addressof(r)` to `val`. Otherwise, destroys `unex`, initializes + //! `val` with `addressof(r)`, and sets `has_val` to `true`; if binding + //! `r` throws, `*this` is left unchanged. + //! \returns `*this`. template requires(!std::is_same_v, expected> && !detail::is_unexpected_specialization>::value && @@ -2601,17 +3445,31 @@ class expected { constexpr expected& operator=(unexpected&& e); // Deleted for reference E with value G: would rebind E& to unexpected's temporary storage. + //! \at expected.ref.assign + //! \group ref-cvt-unexpected-assign-deleted + //! \remarks When `E` is a reference type, an overload taking + //! `unexpected` for a non-reference `G` is defined as deleted: it + //! would rebind `unex` to `unexpected`'s temporary storage. template requires(std::is_reference_v && !std::is_reference_v) constexpr expected& operator=(const unexpected&) = BEMAN_EXPECTED_DELETE_MSG( "expected: cannot assign from unexpected; the value would dangle — use unexpected"); + //! \at expected.ref.assign + //! \also ref-cvt-unexpected-assign-deleted template requires(std::is_reference_v && !std::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 + //! \at expected.ref.assign + //! \constraints `is_constructible_v` is `true` and + //! `reference_constructs_from_temporary_v` is `false`. + //! \effects Rebinds `*this` to refer to the object bound by `T& r = + //! std::forward(u);`: if `has_value()` is `false`, destroys `unex` + //! first. Sets `val` to `addressof(r)` and `has_val` to `true`. + //! \returns `*val`. template requires(std::is_constructible_v && !detail::reference_constructs_from_temporary_v) constexpr T& emplace(U&& u) noexcept(std::is_nothrow_constructible_v); @@ -2620,10 +3478,20 @@ class expected { // Swap // ------------------------------------------------------------------------- + //! \at expected.ref.swap + //! \effects Exchanges the states of `*this` and `rhs`. When both hold + //! values, exchanges `val` and `rhs.val` — the referenced objects are + //! not swapped. Otherwise behaves as the primary template's `swap` + //! does for the error. + //! \remarks The exception specification is equivalent to + //! `is_nothrow_move_constructible_v && (is_reference_v || + //! is_nothrow_swappable_v)`. constexpr void swap(expected& rhs) noexcept(std::is_nothrow_move_constructible_v && (std::is_reference_v || std::is_nothrow_swappable_v)) requires((std::is_reference_v || std::is_swappable_v) && std::is_move_constructible_v); + //! \at expected.ref.swap + //! \effects Equivalent to `x.swap(y)`. friend constexpr void swap(expected& x, expected& y) noexcept(noexcept(x.swap(y))) requires((std::is_reference_v || std::is_swappable_v) && std::is_move_constructible_v) { @@ -2720,6 +3588,17 @@ class expected { // Equality operators (hidden friends) // ------------------------------------------------------------------------- + //! \at expected.ref.eq + //! \mandates `!is_void_v` is `true`. The expression `*x == *y` is + //! well-formed and its result is convertible to `bool`. The expression + //! `x.error() == y.error()` is well-formed and its result is + //! convertible to `bool`. + //! \returns If `x.has_value() != y.has_value()`, `false`; otherwise, if + //! `x.has_value()` is `true`, `*x == *y`; otherwise `x.error() == + //! y.error()`. + //! \remarks The equality operators behave as specified for the primary + //! template, comparing referents through `operator*` and errors + //! through `error()`. template requires(!std::is_void_v) friend constexpr bool operator==(const expected& x, const expected& y) { @@ -2730,29 +3609,47 @@ class expected { return x.error() == y.error(); } + //! \at expected.ref.eq + //! \mandates `T2` is not a specialization of `expected`. The expression + //! `*x == val` is well-formed and its result is convertible to `bool`. + //! \returns `x.has_value() && static_cast(*x == val)`. template requires(!detail::is_expected_specialization::value) friend constexpr bool operator==(const expected& x, const T2& val) { return x.has_value() && static_cast(*x == val); } + //! \at expected.ref.eq + //! \mandates The expression `x.error() == e.error()` is well-formed and + //! its result is convertible to `bool`. + //! \returns `!x.has_value() && static_cast(x.error() == + //! e.error())`. template friend constexpr bool operator==(const expected& x, const unexpected& e) { return !x.has_value() && static_cast(x.error() == e.error()); } private: + //! \expos bool has_val_; union { - T* val_; + //! \expos + T* val_; + //! \expos unexpected unex_; }; }; -// ============================================================================= -// Out-of-line constructor definitions -// ============================================================================= +// \rSec3[expected.ref.cons]{Constructors} +//! \group ref-copy-move-ctor +//! \effects If `rhs.has_value()` is `true`, initializes `val` with +//! `rhs.val`, so that `*this` and `rhs` refer to the same object; +//! otherwise, initializes `unex` with `rhs.unex`. +//! \ensures `rhs.has_value() == this->has_value()`. +//! \remarks This constructor is trivial if the corresponding constructor +//! of `E` is trivial, and is defined as deleted unless +//! `is_copy_constructible_v` is `true`. template constexpr expected::expected(const expected& rhs) noexcept(std::is_nothrow_copy_constructible_v) requires(std::is_copy_constructible_v && !std::is_trivially_copy_constructible_v) @@ -2763,6 +3660,7 @@ constexpr expected::expected(const expected& rhs) noexcept(std::is_nothro std::construct_at(std::addressof(unex_), rhs.unex_); } +//! \also ref-copy-move-ctor template constexpr expected::expected(expected&& rhs) noexcept(std::is_nothrow_move_constructible_v) requires(std::is_move_constructible_v && !std::is_trivially_move_constructible_v) @@ -2773,6 +3671,14 @@ constexpr expected::expected(expected&& rhs) noexcept(std::is_nothrow_mov std::construct_at(std::addressof(unex_), std::move(rhs.unex_)); } +//! \group ref-cvt-copy-ctor +//! \constraints `is_constructible_v` is `true`; +//! `reference_constructs_from_temporary_v` is `false`; and +//! `is_constructible_v` is `true`. +//! \effects If `rhs.has_value()` is `true`, initializes `val` with +//! `addressof(*rhs)`, so that `*this` refers to the object referred to by +//! `rhs`; otherwise, initializes `unex` with the error of `rhs`. No object +//! referred to by `rhs` is moved from. template template requires(!std::is_reference_v && std::is_constructible_v && std::is_constructible_v && @@ -2786,6 +3692,7 @@ constexpr expected::expected(const expected& rhs) : has_val_(rhs.h } } +//! \also ref-cvt-copy-ctor template template requires(!std::is_reference_v && std::is_constructible_v && std::is_constructible_v && @@ -2799,6 +3706,14 @@ constexpr expected::expected(expected&& rhs) : has_val_(rhs.has_va } } +//! \group ref-cvt-copy-ctor-ref +//! \constraints `is_constructible_v` is `true`; +//! `reference_constructs_from_temporary_v` is `false`; `G` is a +//! reference type; and `is_convertible_v` is `true`. +//! \effects If `rhs.has_value()` is `true`, initializes `val` with +//! `addressof(*rhs)`, so that `*this` refers to the object referred to by +//! `rhs`; otherwise, initializes `unex` with the error of `rhs`. No object +//! referred to by `rhs` is moved from. template template requires(std::is_reference_v && std::is_reference_v && std::is_constructible_v && @@ -2812,6 +3727,7 @@ constexpr expected::expected(const expected& rhs) : has_val_(rhs.h } } +//! \also ref-cvt-copy-ctor-ref template template requires(std::is_reference_v && std::is_reference_v && std::is_constructible_v && @@ -2825,6 +3741,10 @@ constexpr expected::expected(expected&& rhs) : has_val_(rhs.has_va } } +//! \group ref-cvt-unexpected-ctor +//! \constraints `is_constructible_v` is `true`. +//! \effects Initializes `unex` with the error of `e`. +//! \ensures `has_value()` is `false`. template template requires(!std::is_reference_v && std::is_constructible_v) @@ -2832,6 +3752,7 @@ constexpr expected::expected(const unexpected& e) : has_val_(false) { std::construct_at(std::addressof(unex_), e.error()); } +//! \also ref-cvt-unexpected-ctor template template requires(!std::is_reference_v && std::is_constructible_v) @@ -2839,6 +3760,13 @@ constexpr expected::expected(unexpected&& e) : has_val_(false) { std::construct_at(std::addressof(unex_), std::move(e).error()); } +//! \group ref-cvt-unexpected-ctor-ref +//! \constraints `is_reference_v` is `true`; `is_convertible_v` is +//! `true`; and `reference_constructs_from_temporary_v` is `false`. +//! \effects Initializes `unex` with the error of `e`. +//! \ensures `has_value()` is `false`. +//! \remarks This constructor never throws: the referent is bound, not +//! copied. template template requires(std::is_reference_v && std::is_reference_v && std::is_constructible_v && @@ -2847,6 +3775,7 @@ constexpr expected::expected(const unexpected& e) noexcept : has_val_( std::construct_at(std::addressof(unex_), e.error()); } +//! \also ref-cvt-unexpected-ctor-ref template template requires(std::is_reference_v && std::is_reference_v && std::is_constructible_v && @@ -2855,6 +3784,10 @@ constexpr expected::expected(unexpected&& e) noexcept : has_val_(false std::construct_at(std::addressof(unex_), e.error()); } +//! \constraints `is_constructible_v` is `true`. +//! \effects Direct-non-list-initializes `unex` with `in_place` and +//! `std::forward(args)...`. +//! \ensures `has_value()` is `false`. template template requires(std::is_constructible_v && !detail::unexpect_dangles_v) @@ -2862,6 +3795,14 @@ constexpr expected::expected(unexpect_t, Args&&... args) : has_val_(false std::construct_at(std::addressof(unex_), std::in_place, std::forward(args)...); } +//! \constraints `is_reference_v` is `false`, and `is_constructible_v&, Args...>` is `true`. +//! \effects Direct-non-list-initializes `unex` with `in_place`, `il`, and +//! `std::forward(args)...`. +//! \ensures `has_value()` is `false`. +//! \remarks An overload with the same parameter types is defined as +//! deleted when `E` is an lvalue reference type. An initializer list +//! cannot provide the required long-lived error referent. template template requires(!std::is_reference_v && std::is_constructible_v&, Args...>) @@ -2869,10 +3810,11 @@ constexpr expected::expected(unexpect_t, std::initializer_list il, Arg std::construct_at(std::addressof(unex_), std::in_place, il, std::forward(args)...); } -// ============================================================================= -// Out-of-line destructor -// ============================================================================= +// \rSec3[expected.ref.dtor]{Destructor} +//! \effects If `has_value()` is `false`, destroys `unex`. `T` is not +//! destroyed; `*this` never owns the object it refers to. +//! \remarks This destructor is trivial if `E` is trivially destructible. template constexpr expected::~expected() requires(!std::is_trivially_destructible_v) @@ -2881,10 +3823,19 @@ constexpr expected::~expected() std::destroy_at(std::addressof(unex_)); } -// ============================================================================= -// Out-of-line assignment definitions -// ============================================================================= +// \rSec3[expected.ref.assign]{Assignment} +//! \group ref-copy-move-assign +//! \effects If `rhs.has_value()` is `true`: if `has_value()` is `true`, +//! assigns `rhs.val` to `val`; otherwise destroys `unex` and initializes +//! `val` with `rhs.val`. If `rhs.has_value()` is `false`, the error of +//! `rhs` is assigned to or used to initialize `unex`, as for the primary +//! template. In every case `*this` comes to refer to the object `rhs` +//! refers to, or to hold the error of `rhs`. +//! \returns `*this`. +//! \remarks This operator is defined as deleted unless +//! `is_copy_assignable_v` is `true` and `is_copy_constructible_v` +//! is `true`. template constexpr expected& expected::operator=(const expected& rhs) noexcept(std::is_nothrow_copy_constructible_v && @@ -2908,6 +3859,7 @@ expected::operator=(const expected& rhs) noexcept(std::is_nothrow_copy_co return *this; } +//! \also ref-copy-move-assign template constexpr expected& expected::operator=(expected&& rhs) noexcept(std::is_nothrow_move_constructible_v && @@ -2931,6 +3883,12 @@ expected::operator=(expected&& rhs) noexcept(std::is_nothrow_move_constru return *this; } +//! \group ref-cvt-unexpected-assign +//! \constraints `is_constructible_v` is `true` and +//! `is_assignable_v` is `true`. +//! \effects Makes `*this` hold the error of `e`, reinitializing `unex` +//! from `e` rather than assigning through it. +//! \returns `*this`. template template requires(!std::is_reference_v && std::is_constructible_v && std::is_assignable_v) @@ -2944,6 +3902,7 @@ constexpr expected& expected::operator=(const unexpected& e) { return *this; } +//! \also ref-cvt-unexpected-assign template template requires(!std::is_reference_v && std::is_constructible_v && std::is_assignable_v) @@ -2959,6 +3918,16 @@ constexpr expected& expected::operator=(unexpected&& e) { // Rebinding assignment for reference E from reference G. val_ is a T* (trivially destructible), // so no destroy is needed; repoint unex_ via construct_at (not `unex_.error() = ...`). +//! \group ref-cvt-unexpected-assign-ref +//! \constraints `is_reference_v` is `true`; `is_convertible_v` is +//! `true`; and `reference_constructs_from_temporary_v` is `false`. +//! \effects Makes `*this` hold the error of `e`, reinitializing `unex` +//! from `e` rather than assigning through it. `unex.error()` thereafter +//! refers to the same object as `e.error()`; the previously referenced +//! object, if any, is not modified. +//! \returns `*this`. +//! \remarks This operator never throws: the referent is bound, not +//! copied. template template requires(std::is_reference_v && std::is_reference_v && std::is_constructible_v && @@ -2969,6 +3938,7 @@ constexpr expected& expected::operator=(const unexpected& e) { return *this; } +//! \also ref-cvt-unexpected-assign-ref template template requires(std::is_reference_v && std::is_reference_v && std::is_constructible_v && @@ -2979,6 +3949,12 @@ constexpr expected& expected::operator=(unexpected&& e) { return *this; } +//! \constraints `is_constructible_v` is `true` and +//! `reference_constructs_from_temporary_v` is `false`. +//! \effects Rebinds `*this` to refer to the object bound by `T& r = +//! std::forward(u);`: if `has_value()` is `false`, destroys `unex` +//! first. Sets `val` to `addressof(r)` and `has_val` to `true`. +//! \returns `*val`. template template requires(std::is_constructible_v && !detail::reference_constructs_from_temporary_v) @@ -2992,10 +3968,15 @@ constexpr T& expected::emplace(U&& u) noexcept(std::is_nothrow_constructi return *val_; } -// ============================================================================= -// Out-of-line swap definition -// ============================================================================= +// \rSec3[expected.ref.swap]{Swap} +//! \effects Exchanges the states of `*this` and `rhs`. When both hold +//! values, exchanges `val` and `rhs.val` — the referenced objects are not +//! swapped. Otherwise behaves as the primary template's `swap` does for +//! the error. +//! \remarks The exception specification is equivalent to +//! `is_nothrow_move_constructible_v && (is_reference_v || +//! is_nothrow_swappable_v)`. template constexpr void expected::swap(expected& rhs) noexcept(std::is_nothrow_move_constructible_v && (std::is_reference_v || @@ -3020,10 +4001,13 @@ constexpr void expected::swap(expected& rhs) noexcept(std::is_nothrow_mov } } -// ============================================================================= -// Out-of-line observer definitions -// ============================================================================= +// \rSec3[expected.ref.obs]{Observers} +//! \expects `has_value()` is `true`. +//! \returns `val`. +//! \remarks This is a `const` member function that returns a non-`const` +//! `T*`; the constness of `*this` does not propagate to the referenced +//! object. For deep `const`, use `expected`. template constexpr T* expected::operator->() const noexcept { #if defined(BEMAN_EXPECTED_HARDENED) @@ -3033,6 +4017,11 @@ constexpr T* expected::operator->() const noexcept { return val_; } +//! \expects `has_value()` is `true`. +//! \returns `*val`. +//! \remarks This is a `const` member function that returns a non-`const` +//! `T&`; the constness of `*this` does not propagate to the referenced +//! object. For deep `const`, use `expected`. template constexpr T& expected::operator*() const noexcept { #if defined(BEMAN_EXPECTED_HARDENED) @@ -3042,16 +4031,22 @@ constexpr T& expected::operator*() const noexcept { return *val_; } +//! \group ref-obs-bool +//! \returns `has_val`. template constexpr expected::operator bool() const noexcept { return has_val_; } +//! \also ref-obs-bool template constexpr bool expected::has_value() const noexcept { return has_val_; } +//! \returns `*val` if `has_value()` is `true`. +//! \throws `bad_expected_access(as_const(error()))` if `has_value()` is +//! `false`. template constexpr T& expected::value() const& { static_assert(std::is_copy_constructible_v, "value() requires is_copy_constructible_v"); @@ -3060,6 +4055,9 @@ constexpr T& expected::value() const& { return *val_; } +//! \returns `*val` if `has_value()` is `true`. +//! \throws `bad_expected_access(std::move(error()))` if `has_value()` is +//! `false`. template constexpr T& expected::value() && { if constexpr (std::is_reference_v) { @@ -3073,6 +4071,9 @@ constexpr T& expected::value() && { return *val_; } +//! \group ref-obs-error-lval +//! \expects `has_value()` is `false`. +//! \returns `unex.error()`. template constexpr const E& expected::error() const& noexcept { #if defined(BEMAN_EXPECTED_HARDENED) @@ -3082,6 +4083,7 @@ constexpr const E& expected::error() const& noexcept { return unex_.error(); } +//! \also ref-obs-error-lval template constexpr E& expected::error() & noexcept { #if defined(BEMAN_EXPECTED_HARDENED) @@ -3091,6 +4093,9 @@ constexpr E& expected::error() & noexcept { return unex_.error(); } +//! \group ref-obs-error-rval +//! \expects `has_value()` is `false`. +//! \returns `std::move(unex).error()`. template constexpr const E&& expected::error() const&& noexcept { #if defined(BEMAN_EXPECTED_HARDENED) @@ -3100,6 +4105,7 @@ constexpr const E&& expected::error() const&& noexcept { return std::move(unex_).error(); } +//! \also ref-obs-error-rval template constexpr E&& expected::error() && noexcept { #if defined(BEMAN_EXPECTED_HARDENED) @@ -3109,6 +4115,11 @@ constexpr E&& expected::error() && noexcept { return std::move(unex_).error(); } +//! \mandates `is_convertible_v>` and +//! `is_convertible_v>` are `true`. +//! \returns `has_value() ? static_cast>(*val) : +//! static_cast>(std::forward(def))`. The result is an +//! object, never a reference. template template requires(std::is_object_v && !std::is_array_v) @@ -3121,6 +4132,8 @@ constexpr std::remove_cv_t expected::value_or(U&& def) const { return static_cast(std::forward(def)); } +//! \returns `std::forward(def)` if `has_value()` is `true`, `error()` +//! otherwise. The result is an object, never a reference. template template requires(std::is_copy_constructible_v>> && @@ -3131,6 +4144,9 @@ constexpr typename expected::error_value_type expected::error_or(G return static_cast(std::forward(def)); } +//! \returns `std::forward(def)` if `has_value()` is `true`, +//! `std::move(error())` otherwise. The result is an object, never a +//! reference. template template requires(std::is_move_constructible_v>> && @@ -3141,10 +4157,21 @@ constexpr typename expected::error_value_type expected::error_or(G return static_cast(std::forward(def)); } -// ============================================================================= -// Out-of-line monadic operation definitions -// ============================================================================= - +// \rSec3[expected.ref.monadic]{Monadic operations} + +//! \group ref-monadic-and-then-lval +//! \constraints `is_constructible_v` is `true`. +//! \mandates `remove_cvref_t>` is a specialization +//! of `expected` and its `error_type` is the same type as `E`. +//! \effects Equivalent to: `if (has_value()) return +//! invoke(std::forward(f), *val); else return U(unexpect, error());` +//! where `U` is `remove_cvref_t>`. +//! \remarks The member templates `and_then`, `or_else`, `transform`, and +//! `transform_error` behave as specified for the primary template, with +//! one difference: the value is passed to the callable as `T&` for every +//! ref-qualification of `*this`. An rvalue `expected` does not pass +//! its referent as an rvalue; the object referred to is never moved from +//! by these operations. template template requires std::is_constructible_v @@ -3159,6 +4186,15 @@ constexpr auto expected::and_then(F&& f) & { return U(unexpect, unex_.error()); } +//! \group ref-monadic-and-then-rval +//! \constraints `is_constructible_v` is +//! `true`. +//! \mandates `remove_cvref_t>` is a specialization +//! of `expected` and its `error_type` is the same type as `E`. +//! \effects Equivalent to: `if (has_value()) return +//! invoke(std::forward(f), *val); else return U(unexpect, +//! std::move(error()));` where `U` is `remove_cvref_t>`. template template requires std::is_constructible_v @@ -3173,6 +4209,7 @@ constexpr auto expected::and_then(F&& f) && { return U(unexpect, std::move(unex_).error()); } +//! \also ref-monadic-and-then-lval template template requires std::is_constructible_v @@ -3187,6 +4224,7 @@ constexpr auto expected::and_then(F&& f) const& { return U(unexpect, unex_.error()); } +//! \also ref-monadic-and-then-rval template template requires std::is_constructible_v @@ -3201,6 +4239,13 @@ constexpr auto expected::and_then(F&& f) const&& { return U(unexpect, std::move(unex_).error()); } +//! \group ref-monadic-or-else-lval +//! \mandates `remove_cvref_t>` is a +//! specialization of `expected` and its `value_type` is the same type as +//! `T&`. +//! \effects Equivalent to: `if (has_value()) return G(*val); else return +//! invoke(std::forward(f), error());` where `G` is +//! `remove_cvref_t>`. template template constexpr auto expected::or_else(F&& f) & { @@ -3213,6 +4258,13 @@ constexpr auto expected::or_else(F&& f) & { return std::invoke(std::forward(f), unex_.error()); } +//! \group ref-monadic-or-else-rval +//! \mandates `remove_cvref_t>` is a specialization of `expected` and +//! its `value_type` is the same type as `T&`. +//! \effects Equivalent to: `if (has_value()) return G(*val); else return +//! invoke(std::forward(f), std::move(error()));` where `G` is +//! `remove_cvref_t>`. template template constexpr auto expected::or_else(F&& f) && { @@ -3225,6 +4277,7 @@ constexpr auto expected::or_else(F&& f) && { return std::invoke(std::forward(f), std::move(unex_).error()); } +//! \also ref-monadic-or-else-lval template template constexpr auto expected::or_else(F&& f) const& { @@ -3237,6 +4290,7 @@ constexpr auto expected::or_else(F&& f) const& { return std::invoke(std::forward(f), unex_.error()); } +//! \also ref-monadic-or-else-rval template template constexpr auto expected::or_else(F&& f) const&& { @@ -3249,6 +4303,12 @@ constexpr auto expected::or_else(F&& f) const&& { return std::invoke(std::forward(f), std::move(unex_).error()); } +//! \group ref-monadic-transform-lval +//! \constraints `is_constructible_v` is `true`. +//! \effects Equivalent to: `if (!has_value()) return U(unexpect, +//! error()); else return expected(in_place, +//! invoke(std::forward(f), *val));` where `U2` is +//! `remove_cv_t>` and `U` is `expected`. template template requires std::is_constructible_v @@ -3274,6 +4334,13 @@ constexpr auto expected::transform(F&& f) & { } } +//! \group ref-monadic-transform-rval +//! \constraints `is_constructible_v` is +//! `true`. +//! \effects Equivalent to: `if (!has_value()) return U(unexpect, +//! std::move(error())); else return expected(in_place, +//! invoke(std::forward(f), *val));` where `U2` is +//! `remove_cv_t>` and `U` is `expected`. template template requires std::is_constructible_v @@ -3299,6 +4366,7 @@ constexpr auto expected::transform(F&& f) && { } } +//! \also ref-monadic-transform-lval template template requires std::is_constructible_v @@ -3324,6 +4392,7 @@ constexpr auto expected::transform(F&& f) const& { } } +//! \also ref-monadic-transform-rval template template requires std::is_constructible_v @@ -3349,6 +4418,11 @@ constexpr auto expected::transform(F&& f) const&& { } } +//! \group ref-monadic-transform-error-lval +//! \effects Equivalent to: `if (has_value()) return G(*val); else return +//! expected(unexpect, invoke(std::forward(f), error()));` +//! where `G2` is `remove_cv_t>` and +//! `G` is `expected`. template template constexpr auto expected::transform_error(F&& f) & { @@ -3363,6 +4437,12 @@ constexpr auto expected::transform_error(F&& f) & { return expected(unexpect, std::invoke(std::forward(f), unex_.error())); } +//! \group ref-monadic-transform-error-rval +//! \effects Equivalent to: `if (has_value()) return G(*val); else return +//! expected(unexpect, invoke(std::forward(f), +//! std::move(error())));` where `G2` is +//! `remove_cv_t>` and +//! `G` is `expected`. template template constexpr auto expected::transform_error(F&& f) && { @@ -3377,6 +4457,7 @@ constexpr auto expected::transform_error(F&& f) && { return expected(unexpect, std::invoke(std::forward(f), std::move(unex_).error())); } +//! \also ref-monadic-transform-error-lval template template constexpr auto expected::transform_error(F&& f) const& { @@ -3391,6 +4472,7 @@ constexpr auto expected::transform_error(F&& f) const& { return expected(unexpect, std::invoke(std::forward(f), unex_.error())); } +//! \also ref-monadic-transform-error-rval template template constexpr auto expected::transform_error(F&& f) const&& { @@ -3405,6 +4487,8 @@ constexpr auto expected::transform_error(F&& f) const&& { return expected(unexpect, std::invoke(std::forward(f), std::move(unex_).error())); } +// \rSec3[expected.ref.eq]{Equality operators} + } // namespace expected } // namespace beman diff --git a/include/beman/expected/unexpected.hpp b/include/beman/expected/unexpected.hpp index dbfc428..673eb9b 100644 --- a/include/beman/expected/unexpected.hpp +++ b/include/beman/expected/unexpected.hpp @@ -25,9 +25,11 @@ namespace beman { namespace expected { // [expected.unexpect] +//! \elsewhere struct unexpect_t { explicit unexpect_t() = default; }; +//! \elsewhere inline constexpr unexpect_t unexpect{}; // Forward declaration for is_unexpected_specialization trait @@ -35,6 +37,7 @@ template class unexpected; namespace detail { +//! \expos template struct is_unexpected_specialization : std::false_type {}; template @@ -45,15 +48,24 @@ struct is_unexpected_specialization> : std::true_type {}; // its builtin __reference_converts_from_temporary is absent on Clang 18, which has only the // __reference_constructs_from_temporary builtin.) #ifdef __cpp_lib_reference_from_temporary +//! \elsewhere using std::reference_constructs_from_temporary_v; #elif __has_builtin(__reference_constructs_from_temporary) +//! \elsewhere template inline constexpr bool reference_constructs_from_temporary_v = __reference_constructs_from_temporary(T, U); #endif } // namespace detail -// [expected.unexpected] +// \rSec2[expected.unexpected]{Class template unexpected} +// \rSec3[expected.un.general]{General} +//! \mandates A program that instantiates the definition of `unexpected` for +//! a non-object type other than an lvalue reference type, an array type, a +//! specialization of `unexpected`, or a cv-qualified type is ill-formed. +//! \remarks Subclause \iref{expected.unexpected} describes the class +//! template `unexpected` that represents unexpected objects stored in +//! `expected` objects. template class unexpected { // [expected.un.general] para 2: ill-formed instantiations @@ -67,18 +79,37 @@ class unexpected { constexpr unexpected(const unexpected&) = default; constexpr unexpected(unexpected&&) = default; + //! \at expected.un.cons + //! \constraints + //! \item `is_same_v, unexpected>` is `false`; and + //! \item `is_same_v, in_place_t>` is `false`; and + //! \item `is_constructible_v` is `true`. + //! \effects Direct-non-list-initializes `unex` with + //! `std::forward(e)`. + //! \throws Any exception thrown by the initialization of `unex`. template requires(!std::is_same_v, unexpected> && !std::is_same_v, std::in_place_t> && std::is_constructible_v) constexpr explicit unexpected(Err&& e) noexcept(std::is_nothrow_constructible_v) : unex_(std::forward(e)) {} + //! \at expected.un.cons + //! \constraints `is_constructible_v` is `true`. + //! \effects Direct-non-list-initializes `unex` with + //! `std::forward(args)...`. + //! \throws Any exception thrown by the initialization of `unex`. template requires std::is_constructible_v constexpr explicit unexpected(std::in_place_t, Args&&... args) noexcept(std::is_nothrow_constructible_v) : unex_(std::forward(args)...) {} + //! \at expected.un.cons + //! \constraints `is_constructible_v&, Args...>` is + //! `true`. + //! \effects Direct-non-list-initializes `unex` with `il, + //! std::forward(args)...`. + //! \throws Any exception thrown by the initialization of `unex`. template requires std::is_constructible_v&, Args...> constexpr explicit unexpected(std::in_place_t, std::initializer_list il, Args&&... args) noexcept( @@ -88,21 +119,40 @@ class unexpected { constexpr unexpected& operator=(const unexpected&) = default; constexpr unexpected& operator=(unexpected&&) = default; - constexpr const E& error() const& noexcept { return unex_; } - constexpr E& error() & noexcept { return unex_; } + //! \at expected.un.obs + //! \group un-error + //! \returns `unex`. + constexpr const E& error() const& noexcept { return unex_; } + //! \at expected.un.obs + //! \also un-error + constexpr E& error() & noexcept { return unex_; } + //! \at expected.un.obs + //! \group un-error-rvalue + //! \returns `std::move(unex)`. constexpr const E&& error() const&& noexcept { return std::move(unex_); } - constexpr E&& error() && noexcept { return std::move(unex_); } + //! \at expected.un.obs + //! \also un-error-rvalue + constexpr E&& error() && noexcept { return std::move(unex_); } + //! \at expected.un.swap + //! \mandates `is_swappable_v` is `true`. + //! \effects Equivalent to: `using std::swap; swap(unex, other.unex);` constexpr void swap(unexpected& other) noexcept(std::is_nothrow_swappable_v) { using std::swap; swap(unex_, other.unex_); } + //! \at expected.un.eq + //! \mandates The expression `x.error() == y.error()` is well-formed and + //! its result is convertible to `bool`. + //! \returns `x.error() == y.error()`. template friend constexpr bool operator==(const unexpected& x, const unexpected& y) { return x.unex_ == y.error(); } + //! \at expected.un.swap + //! \effects Equivalent to `x.swap(y)`. friend constexpr void swap(unexpected& x, unexpected& y) noexcept(noexcept(x.swap(y))) requires std::is_swappable_v { @@ -110,15 +160,53 @@ class unexpected { } private: + //! \expos E unex_; }; template unexpected(E) -> unexpected; -// [expected.unexpected], partial specialization for reference E +// \rSec3[expected.un.cons]{Constructors} +// \rSec3[expected.un.obs]{Observers} +// \rSec3[expected.un.swap]{Swap} +// \rSec3[expected.un.eq]{Equality operator} + +// \rSec3[expected.un.ref]{Partial specialization unexpected} // Stores a pointer to the referenced object; keeps expected<> from needing a // separate set of specializations just to hold a reference error type. +//! \at expected.un.ref +//! \mandates A program that instantiates the definition of `unexpected` +//! for an array type or a specialization of `unexpected` is ill-formed. +//! \remarks An object of type `unexpected` holds a pointer to an object +//! of type `E`. The referenced object is not owned by the `unexpected` +//! object. Unlike the primary template, `E` may be a cv-qualified type. +//! \verbatim-synopsis +//! template +//! class unexpected { +//! public: +//! constexpr unexpected(const unexpected&) = default; +//! constexpr unexpected(unexpected&&) = default; +//! template +//! constexpr explicit unexpected(G&&) noexcept; +//! template +//! constexpr explicit unexpected(in_place_t, G&&) noexcept; +//! +//! constexpr unexpected& operator=(const unexpected&) = default; +//! constexpr unexpected& operator=(unexpected&&) = default; +//! +//! constexpr E& error() const noexcept; +//! +//! constexpr void swap(unexpected& other) noexcept; +//! +//! template +//! friend constexpr bool operator==(const unexpected&, const unexpected&); +//! +//! friend constexpr void swap(unexpected& x, unexpected& y) noexcept; +//! +//! private: +//! E* unex; // exposition only +//! }; template class unexpected { static_assert(std::is_object_v, @@ -134,19 +222,34 @@ class unexpected { constexpr unexpected(unexpected&&) = default; // Binds E& directly to the referenced object; deleted below when G would bind to a temporary. + //! \at expected.un.ref + //! \constraints + //! \item `is_same_v, unexpected>` is `false`, + //! \item `is_same_v, in_place_t>` is `false`, + //! \item `is_constructible_v` is `true`, and + //! \item `reference_constructs_from_temporary_v` is `false`. + //! \effects Initializes `unex` with + //! `addressof(static_cast(std::forward(e)))`. + //! \remarks A constructor for which + //! `reference_constructs_from_temporary_v` is `true` — one that + //! would bind `E&` to a temporary — is defined as deleted. template requires(!std::is_same_v, unexpected> && !std::is_same_v, std::in_place_t> && std::is_constructible_v && !detail::reference_constructs_from_temporary_v) constexpr explicit unexpected(G&& e) noexcept : ptr_(std::addressof(static_cast(std::forward(e)))) {} - // Deleted: binding would dangle (G materializes a temporary) + // Deleted: binding would dangle (G materializes a temporary). Represented in wording by the + // enabled overload above, whose \remarks says when it's deleted instead. + //! \merge template requires(detail::reference_constructs_from_temporary_v) constexpr unexpected(G&&) = BEMAN_EXPECTED_DELETE_MSG( "unexpected: argument would bind a temporary that dangles; pass an lvalue reference"); - // Deleted catch-all: neither constructible nor a dangling case + // Deleted catch-all: neither constructible nor a dangling case. Not part of the specified + // overload set at all (its constraint is simply the enabled overload's negation). + //! \merge template requires(!std::is_same_v, unexpected> && !std::is_same_v, std::in_place_t> && !std::is_constructible_v && @@ -158,11 +261,18 @@ class unexpected { // construct_at(addressof(unex_), std::in_place, args...) pattern work whether E is a // reference or not. Naturally restricted to arity 1: there is no variadic overload here, // and expected only ever calls this when is_constructible_v already holds. + //! \at expected.un.ref + //! \constraints `is_constructible_v` is `true` and + //! `reference_constructs_from_temporary_v` is `false`. + //! \effects Initializes `unex` with + //! `addressof(static_cast(std::forward(e)))`. template requires(std::is_constructible_v && !detail::reference_constructs_from_temporary_v) constexpr explicit unexpected(std::in_place_t, G&& e) noexcept : ptr_(std::addressof(static_cast(std::forward(e)))) {} + // Deleted: in_place argument would dangle. Represented in wording by the enabled overload above. + //! \merge template requires(detail::reference_constructs_from_temporary_v) constexpr unexpected(std::in_place_t, G&&) = BEMAN_EXPECTED_DELETE_MSG( @@ -173,18 +283,32 @@ class unexpected { // Single overload — shallow-const, matching expected's existing error() style: // there is nothing to move out of a pointer to an external object. + //! \at expected.un.ref + //! \returns `*unex`. + //! \remarks The reference returned is not `const`-qualified even when + //! `*this` is `const`; the constness of the `unexpected` object does + //! not propagate to the referenced object. constexpr E& error() const noexcept { return *ptr_; } + //! \at expected.un.ref + //! \effects Exchanges `unex` and `other.unex`. constexpr void swap(unexpected& other) noexcept { std::swap(ptr_, other.ptr_); } + //! \at expected.un.ref + //! \mandates The expression `x.error() == y.error()` is well-formed and + //! its result is convertible to `bool`. + //! \returns `x.error() == y.error()`. template friend constexpr bool operator==(const unexpected& x, const unexpected& y) { return *x.ptr_ == y.error(); } + //! \at expected.un.ref + //! \effects Equivalent to `x.swap(y)`. friend constexpr void swap(unexpected& x, unexpected& y) noexcept { x.swap(y); } private: + //! \expos(unex) E* ptr_; }; diff --git a/papers/wording/README.md b/papers/wording/README.md new file mode 100644 index 0000000..0706e00 --- /dev/null +++ b/papers/wording/README.md @@ -0,0 +1,73 @@ +# Generated `[expected]` wording + +Everything in this directory is generated from the `//!` docblocks in +`include/beman/expected/{unexpected,bad_expected_access,expected}.hpp` via +[specgen](https://github.com/steve-downey/specgen). Don't hand-edit it; the +header comments are the source of truth. The exception is `preamble.tex`, the +comment block at the top of `expected.tex`, which is prose about this paper and +is written by hand. + +```sh +make wording +``` + +with `specgen` on `PATH`, or `make SPECGEN=/path/to/specgen wording`. The rule +has real prerequisites, so it does nothing when nothing changed, and `make +papers` regenerates the wording before building the PDF — a paper can no longer +be built from stale clauses. + +GNU Make 4.3 or newer. One specgen invocation writes all six fragments, which +the makefile states as a grouped target (`&:`) — a rule 4.2 parses as something +else entirely, and would then run specgen once per fragment. + +specgen writes what it read to `papers/.deps/wording.d`, which the makefile +`-include`s. That is how the dependencies stay honest: it lists every header +each parse touched, including `include/beman/expected/config.hpp`, which none +of the three documented headers names on the command line and which a +hand-maintained prerequisite list would forget. + +If embedded Clang cannot locate the C++ standard library, set +`SPECGEN_GCC_TOOLCHAIN` to the GCC installation prefix before regenerating. + +## `expected.tex` + +All the generated subclauses concatenated in real standard order — +`[expected.unexpected]` (including the new `[expected.un.ref]`) through +`[expected.ref.eq]`. The makefile passes `--base-section-depth 2`, so each +`\rSec` marker comes out at the level the draft writes it at: +`[expected.unexpected]` and its siblings are `\rSec2`, their subclauses +`\rSec3`, numbering `22.8.3`, `22.8.3.1`, ... This file is everything that +sits *inside* the existing `\rSec1[expected]{Expected objects}` in +[the draft](https://github.com/cplusplus/draft)'s `source/utilities.tex` — no +wrapper `\rSec1[expected]`, no `\input` directives — so it's the basis for a +patch there: replace the current `[expected.unexpected]` through +`[expected.void]` subclauses with this file's content and the new +`[expected.ref]` subclause lands after them, in place. + +It does not include `[expected.general]` or `[expected.syn]`: those are prose +that doesn't come from any one declaration. See `papers/expected-new.tex` for +hand-authored versions of both. + +The makefile assembles this file from `fragments/` rather than taking +specgen's own joined output, because the clause order is the draft's and not +the headers'. `bad_expected_access` is the base class of +`bad_expected_access`, so the header must define the specialization first, +while the draft states the primary template first. `$(WORDING_CLAUSES)` in the +repository makefile is where that divergence is written down; it is the only +thing about the paper's shape that lives outside the headers. + +## `fragments/` + +The same content, split one file per top-level clause and named for its stable +name (`expected.unexpected.tex`, `expected.bad.tex`, `expected.bad.void.tex`, +`expected.expected.tex`, `expected.void.tex`, `expected.ref.tex`) — for +`\input` into a standalone paper's own `\rSec1[expected]{Expected objects}` +(see `papers/expected-new.tex`), which is the level `expected.tex` above +assumes already exists. + +specgen also writes a `*.root.tex` per document, holding the declarations that +sit outside every clause: the exposition-only helpers +(*is-unexpected-specialization*, *reinit-expected*, +*converts-from-any-cvref*, and friends). The draft states each of those inline +in the clause that uses it, so they are not part of this paper's wording; the +files are gitignored rather than committed. diff --git a/papers/wording/expected.tex b/papers/wording/expected.tex new file mode 100644 index 0000000..ead4115 --- /dev/null +++ b/papers/wording/expected.tex @@ -0,0 +1,4683 @@ +% papers/wording/expected.tex -*-LaTeX-*- +% SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +% +% Generated from the annotated headers in include/beman/expected/ via specgen. +% Do not edit by hand: change a header's //! docblocks and run `make wording` +% instead. This comment comes from papers/wording/preamble.tex, which is the +% one part of this file a person writes. +% +% --base-section-depth 2 puts each \rSec marker at the level the draft +% writes it at: [expected.unexpected] and its siblings are \rSec2, their +% subclauses \rSec3, numbering 22.8.3, 22.8.3.1, ... just as in +% source/utilities.tex. This file is therefore everything that sits +% *inside* the existing \rSec1[expected]{Expected objects} in the draft's +% source/utilities.tex, in clause order, ready to replace the current +% [expected.unexpected] through [expected.void] subclauses and add the new +% [expected.ref] one after them -- no \rSec1[expected] wrapper and no +% \input directives. [expected.general] and [expected.syn] are prose, not +% generated from any one declaration; see papers/expected-new.tex. + +\rSec2[expected.unexpected]{Class template unexpected} + +\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 +}; +\end{codeblock} + +\begin{itemdescr} +\pnum +\mandates +A program that instantiates the definition of \tcode{unexpected} for a non-object type other than an lvalue reference type, an array type, a specialization of \tcode{unexpected}, or a cv-qualified type is ill-formed. + +\pnum +\remarks +Subclause \iref{expected.unexpected} describes the class template \tcode{unexpected} that represents unexpected objects stored in \tcode{expected} objects. +\end{itemdescr} + +\rSec3[expected.un.cons]{Constructors} + +\indexlibraryctor{unexpected}% +\begin{itemdecl} +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); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\begin{itemize} +\item \tcode{is_same_v, unexpected>} is \tcode{false}; and +\item \tcode{is_same_v, in_place_t>} is \tcode{false}; and +\item \tcode{is_constructible_v} is \tcode{true}. +\end{itemize} + +\pnum +\effects +Direct-non-list-initializes \tcode{unex} with \tcode{std::forward(e)}. + +\pnum +\throws +Any exception thrown by the initialization of \tcode{unex}. +\end{itemdescr} + +\indexlibraryctor{unexpected}% +\begin{itemdecl} +template + requires is_constructible_v +constexpr explicit unexpected(in_place_t, Args&&... args) noexcept( + is_nothrow_constructible_v); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\effects +Direct-non-list-initializes \tcode{unex} with \tcode{std::forward(args)...}. + +\pnum +\throws +Any exception thrown by the initialization of \tcode{unex}. +\end{itemdescr} + +\indexlibraryctor{unexpected}% +\begin{itemdecl} +template + requires is_constructible_v&, Args...> +constexpr explicit unexpected( + in_place_t, initializer_list il, + Args&&... args) noexcept(is_nothrow_constructible_v&, + Args...>); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v&, Args...>} is \tcode{true}. + +\pnum +\effects +Direct-non-list-initializes \tcode{unex} with \tcode{il, std::forward(args)...}. + +\pnum +\throws +Any exception thrown by the initialization of \tcode{unex}. +\end{itemdescr} + +\rSec3[expected.un.obs]{Observers} + +\indexlibrarymember{error}{unexpected}% +\begin{itemdecl} +constexpr const E& error() const& noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\tcode{unex}. +\end{itemdescr} + +\indexlibrarymember{error}{unexpected}% +\begin{itemdecl} +constexpr E& error() & noexcept; +\end{itemdecl} + +\indexlibrarymember{error}{unexpected}% +\begin{itemdecl} +constexpr const E&& error() const&& noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\tcode{std::move(unex)}. +\end{itemdescr} + +\indexlibrarymember{error}{unexpected}% +\begin{itemdecl} +constexpr E&& error() && noexcept; +\end{itemdecl} + +\rSec3[expected.un.swap]{Swap} + +\indexlibrarymember{swap}{unexpected}% +\begin{itemdecl} +constexpr void swap(unexpected& other) noexcept(is_nothrow_swappable_v); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{is_swappable_v} is \tcode{true}. + +\pnum +\effects +Equivalent to: \tcode{using std::swap; swap(unex, other.unex);} +\end{itemdescr} + +\indexlibraryglobal{swap}% +\begin{itemdecl} +friend constexpr void swap(unexpected& x, unexpected& y) noexcept(noexcept(x.swap(y))); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_swappable_v} is \tcode{true}. + +\pnum +\effects +Equivalent to \tcode{x.swap(y)}. +\end{itemdescr} + +\rSec3[expected.un.eq]{Equality operator} + +\indexlibraryglobal{operator==}% +\begin{itemdecl} +template +friend constexpr bool operator==(const unexpected& x, const unexpected& y); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +The expression \tcode{x.error() == y.error()} is well-formed and its result is convertible to \tcode{bool}. + +\pnum +\returns +\tcode{x.error() == y.error()}. +\end{itemdescr} + +\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; + + constexpr unexpected& operator=(const unexpected&) = default; + constexpr unexpected& operator=(unexpected&&) = default; + + constexpr E& error() const noexcept; + + constexpr void swap(unexpected& other) noexcept; + + template + friend constexpr bool operator==(const unexpected&, const unexpected&); + + friend constexpr void swap(unexpected& x, unexpected& y) noexcept; + +private: + E* unex; // exposition only +}; +\end{codeblock} + +\begin{itemdescr} +\pnum +\mandates +A program that instantiates the definition of \tcode{unexpected} for an array type or a specialization of \tcode{unexpected} is ill-formed. + +\pnum +\remarks +An object of type \tcode{unexpected} holds a pointer to an object of type \tcode{E}. The referenced object is not owned by the \tcode{unexpected} object. Unlike the primary template, \tcode{E} may be a cv-qualified type. +\end{itemdescr} + +\indexlibraryctor{unexpected}% +\begin{itemdecl} +template + requires(!is_same_v, unexpected> && + !is_same_v, in_place_t> && is_constructible_v && + !reference_constructs_from_temporary_v) +constexpr explicit unexpected(G&& e) noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\begin{itemize} +\item \tcode{is_same_v, unexpected>} is \tcode{false}, +\item \tcode{is_same_v, in_place_t>} is \tcode{false}, +\item \tcode{is_constructible_v} is \tcode{true}, and +\item \tcode{reference_constructs_from_temporary_v} is \tcode{false}. +\end{itemize} + +\pnum +\effects +Initializes \tcode{unex} with \tcode{addressof(static_cast(std::forward(e)))}. + +\pnum +\remarks +A constructor for which \tcode{reference_constructs_from_temporary_v} is \tcode{true} — one that would bind \tcode{E&} to a temporary — is defined as deleted. +\end{itemdescr} + +\indexlibraryctor{unexpected}% +\begin{itemdecl} +template + requires(is_constructible_v && !reference_constructs_from_temporary_v) +constexpr explicit unexpected(in_place_t, G&& e) noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true} and \tcode{reference_constructs_from_temporary_v} is \tcode{false}. + +\pnum +\effects +Initializes \tcode{unex} with \tcode{addressof(static_cast(std::forward(e)))}. +\end{itemdescr} + +\indexlibrarymember{error}{unexpected}% +\begin{itemdecl} +constexpr E& error() const noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\tcode{*unex}. + +\pnum +\remarks +The reference returned is not \tcode{const}-qualified even when \tcode{*this} is \tcode{const}; the constness of the \tcode{unexpected} object does not propagate to the referenced object. +\end{itemdescr} + +\indexlibrarymember{swap}{unexpected}% +\begin{itemdecl} +constexpr void swap(unexpected& other) noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Exchanges \tcode{unex} and \tcode{other.unex}. +\end{itemdescr} + +\indexlibraryglobal{operator==}% +\begin{itemdecl} +template +friend constexpr bool operator==(const unexpected& x, const unexpected& y); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +The expression \tcode{x.error() == y.error()} is well-formed and its result is convertible to \tcode{bool}. + +\pnum +\returns +\tcode{x.error() == y.error()}. +\end{itemdescr} + +\indexlibraryglobal{swap}% +\begin{itemdecl} +friend constexpr void swap(unexpected& x, unexpected& y) noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Equivalent to \tcode{x.swap(y)}. +\end{itemdescr} + +\rSec2[expected.bad]{Class template bad_expected_access} + +\indexlibrarymember{what}{bad_expected_access}% +\begin{itemdecl} +const char* what() const noexcept override; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +An implementation-defined ntbs, which during constant evaluation is encoded with the ordinary literal encoding (\iref{lex.ccon}). +\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 +}; +\end{codeblock} + +\begin{itemdescr} +\pnum +\remarks +The class template \tcode{bad_expected_access} defines the type of objects thrown as exceptions to report the situation where an attempt is made to access the value of an \tcode{expected} object for which \tcode{has_value()} is \tcode{false}. +\end{itemdescr} + +\indexlibraryctor{bad_expected_access}% +\begin{itemdecl} +explicit bad_expected_access(E e); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Initializes \exposid{unex} with \tcode{std::move(e)}. +\end{itemdescr} + +\indexlibrarymember{what}{bad_expected_access}% +\begin{itemdecl} +const char* what() const noexcept override; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +An implementation-defined ntbs, which during constant evaluation is encoded with the ordinary literal encoding (\iref{lex.ccon}). +\end{itemdescr} + +\indexlibrarymember{error}{bad_expected_access}% +\begin{itemdecl} +constexpr E& error() & noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\exposid{unex}. +\end{itemdescr} + +\indexlibrarymember{error}{bad_expected_access}% +\begin{itemdecl} +constexpr const E& error() const& noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\exposid{unex}. +\end{itemdescr} + +\indexlibrarymember{error}{bad_expected_access}% +\begin{itemdecl} +constexpr E&& error() && noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\tcode{std::move(unex)}. +\end{itemdescr} + +\indexlibrarymember{error}{bad_expected_access}% +\begin{itemdecl} +constexpr const E&& error() const&& noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\tcode{std::move(unex)}. +\end{itemdescr} + +\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; +}; +\end{codeblock} + +\rSec2[expected.expected]{Class template expected} + +\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 && + 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_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} +\pnum +\mandates +A program that instantiates the definition of \tcode{expected} with a \tcode{T} that is not a valid value type for \tcode{expected} (that is, \tcode{remove_cv_t} is \tcode{void}, or a complete non-array object type other than \tcode{in_place_t}, \tcode{unexpect_t}, or a specialization of \tcode{unexpected}) is ill-formed. A program that instantiates the definition of \tcode{expected} with an \tcode{E} that is not a valid template argument for \tcode{unexpected} is ill-formed. + +\pnum +\remarks +Any object of type \tcode{expected} either contains a value of type \tcode{T} or a value of type \tcode{E} nested within it. Member \tcode{has_val} indicates whether the \tcode{expected} object contains an object of type \tcode{T}. When \tcode{has_value()} is \tcode{false}, the error is \tcode{unex.error()}. The error is held as an \tcode{unexpected}, and not as an \tcode{E}, so that \tcode{E} may be an lvalue reference type: an \tcode{E&} cannot be a union member, whereas \tcode{unexpected} holds a pointer to an external object. +\end{itemdescr} + +\rSec3[expected.object.cons]{Constructors} + +\indexlibraryctor{expected}% +\begin{itemdecl} +constexpr expected() noexcept(is_nothrow_default_constructible_v); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_default_constructible_v} is \tcode{true}. + +\pnum +\effects +Value-initializes \tcode{val}. + +\pnum +\ensures +\tcode{has_value()} is \tcode{true}. + +\pnum +\throws +Any exception thrown by the initialization of \tcode{val}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +constexpr expected(const expected&) = default; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Direct-non-list-initializes \tcode{val} or \tcode{unex} (matching \tcode{rhs}'s active member) by trivial copy construction. + +\pnum +\ensures +\tcode{rhs.has_value() == this->has_value()}. + +\pnum +\remarks +This constructor is trivial. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +constexpr expected(const expected& rhs) noexcept(is_nothrow_copy_constructible_v && + is_nothrow_copy_constructible_v); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_copy_constructible_v} is \tcode{true}, \tcode{is_copy_constructible_v} is \tcode{true}, and \tcode{(is_trivially_copy_constructible_v && is_trivially_copy_constructible_v)} is \tcode{false}. + +\pnum +\effects +If \tcode{rhs.has_value()} is \tcode{true}, direct-non-list-initializes \tcode{val} with \tcode{*rhs}. Otherwise, direct-non-list-initializes \tcode{unex} with \tcode{rhs.error()}. + +\pnum +\ensures +\tcode{rhs.has_value() == this->has_value()}. + +\pnum +\throws +Any exception thrown by the initialization of \tcode{val} or \tcode{unex}. + +\pnum +\remarks +This constructor is defined as deleted unless \tcode{is_copy_constructible_v} is \tcode{true} and \tcode{is_copy_constructible_v} is \tcode{true} or \tcode{is_reference_v} is \tcode{true}. This constructor is trivial if \tcode{is_trivially_copy_constructible_v} is \tcode{true} and \tcode{is_trivially_copy_constructible_v} is \tcode{true}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +constexpr expected(expected&&) = default; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Direct-non-list-initializes \tcode{val} or \tcode{unex} (matching \tcode{rhs}'s active member) by trivial move construction. + +\pnum +\ensures +\tcode{rhs.has_value() == this->has_value()}. + +\pnum +\remarks +This constructor is trivial. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +constexpr expected(expected&& rhs) noexcept(is_nothrow_move_constructible_v && + is_nothrow_move_constructible_v); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_move_constructible_v} is \tcode{true}, \tcode{is_move_constructible_v} is \tcode{true}, and \tcode{(is_trivially_move_constructible_v && is_trivially_move_constructible_v)} is \tcode{false}. + +\pnum +\effects +If \tcode{rhs.has_value()} is \tcode{true}, direct-non-list-initializes \tcode{val} with \tcode{std::move(*rhs)}. Otherwise, direct-non-list-initializes \tcode{unex} with \tcode{std::move(rhs.error())}. + +\pnum +\ensures +\tcode{rhs.has_value()} is unchanged; \tcode{rhs.has_value() == this->has_value()} is \tcode{true}. + +\pnum +\throws +Any exception thrown by the initialization of \tcode{val} or \tcode{unex}. + +\pnum +\remarks +This constructor is trivial if \tcode{is_trivially_move_constructible_v} is \tcode{true} and \tcode{is_trivially_move_constructible_v} is \tcode{true}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +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); +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); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}; and \tcode{is_constructible_v} is \tcode{true}; and if \tcode{T} is not \tcode{bool}, \tcode{converts-from-any-cvref>} is \tcode{false}; and \tcode{is_constructible_v, expected&>} is \tcode{false}; and \tcode{is_constructible_v, expected>} is \tcode{false}; and \tcode{is_constructible_v, const expected&>} is \tcode{false}; and \tcode{is_constructible_v, const expected>} is \tcode{false}. + +\pnum +\effects +If \tcode{rhs.has_value()}, direct-non-list-initializes \tcode{val} with \tcode{*rhs}. Otherwise, direct-non-list-initializes \tcode{unex} with \tcode{rhs.error()}. + +\pnum +\ensures +\tcode{rhs.has_value()} is unchanged; \tcode{rhs.has_value() == this->has_value()} is \tcode{true}. + +\pnum +\throws +Any exception thrown by the initialization of \tcode{val} or \tcode{unex}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +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); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_reference_v} is \tcode{true} and \tcode{is_convertible_v} is \tcode{true}. + +\pnum +\effects +If \tcode{rhs.has_value()}, direct-non-list-initializes \tcode{val} with \tcode{*rhs}. Otherwise, direct-non-list-initializes \tcode{unex} with \tcode{rhs.error()}. + +\pnum +\ensures +\tcode{rhs.has_value()} is unchanged; \tcode{rhs.has_value() == this->has_value()} is \tcode{true}. + +\pnum +\throws +Any exception thrown by the initialization of \tcode{val} or \tcode{unex}. + +\pnum +\remarks +Unlike the value-\tcode{E} overload above, this overload participates in overload resolution only when \tcode{E} and \tcode{G} are both reference types, so the referenced error object is never copied. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +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); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_same_v, in_place_t>} is \tcode{false}; and \tcode{is_same_v, unexpect_t>} is \tcode{false}; and \tcode{is_same_v, expected>} is \tcode{false}; and \tcode{is_constructible_v} is \tcode{true}; and \tcode{remove_cvref_t} is not a specialization of \tcode{unexpected}; and if \tcode{T} is \tcode{bool}, \tcode{remove_cvref_t} is not a specialization of \tcode{expected}. + +\pnum +\effects +Direct-non-list-initializes \tcode{val} with \tcode{std::forward(v)}. + +\pnum +\ensures +\tcode{has_value()} is \tcode{true}. + +\pnum +\throws +Any exception thrown by the initialization of \tcode{val}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +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); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\effects +Direct-non-list-initializes \tcode{unex} with \tcode{std::forward(e.error())}. + +\pnum +\ensures +\tcode{has_value()} is \tcode{false}. + +\pnum +\throws +Any exception thrown by the initialization of \tcode{unex}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +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; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_reference_v} is \tcode{true}; and \tcode{is_constructible_v} is \tcode{true}; and \tcode{reference_constructs_from_temporary_v} is \tcode{false}. + +\pnum +\effects +Initializes \tcode{unex} with \tcode{e.error()}. + +\pnum +\ensures +\tcode{has_value()} is \tcode{false}. + +\pnum +\remarks +This constructor never throws: the referent is bound, not copied. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + requires(is_reference_v && !is_reference_v) +constexpr expected(const unexpected&); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\remarks +When \tcode{E} is a reference type, an overload taking \tcode{unexpected} for a non-reference \tcode{G} is defined as deleted: the referent would live inside the (possibly temporary) source \tcode{unexpected} object, and binding \tcode{E&} to it would dangle. Use an \tcode{unexpected} holding an external object instead. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + requires is_constructible_v +constexpr explicit expected(in_place_t, Args&&... args); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\effects +Direct-non-list-initializes \tcode{val} with \tcode{std::forward(args)...}. + +\pnum +\ensures +\tcode{has_value()} is \tcode{true}. + +\pnum +\throws +Any exception thrown by the initialization of \tcode{val}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + requires is_constructible_v&, Args...> +constexpr explicit expected(in_place_t, initializer_list il, Args&&... args); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v&, Args...>} is \tcode{true}. + +\pnum +\effects +Direct-non-list-initializes \tcode{val} with \tcode{il, std::forward(args)...}. + +\pnum +\ensures +\tcode{has_value()} is \tcode{true}. + +\pnum +\throws +Any exception thrown by the initialization of \tcode{val}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + requires(is_constructible_v && !@\exposidnc{unexpect-dangles-v}@) +constexpr explicit expected(unexpect_t, Args&&... args); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\effects +Direct-non-list-initializes \tcode{unex} with \tcode{std::forward(args)...}. + +\pnum +\ensures +\tcode{has_value()} is \tcode{false}. + +\pnum +\throws +Any exception thrown by the initialization of \tcode{unex}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + requires(@\exposidnc{unexpect-dangles-v}@) +constexpr expected(unexpect_t, Args&&...); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\remarks +When \tcode{E} is a reference type, an overload with the same parameter types is defined as deleted if the single argument would bind \tcode{E&} to a temporary, or if it is otherwise not usable to construct \tcode{E}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + requires(!is_reference_v && is_constructible_v&, Args...>) +constexpr explicit expected(unexpect_t, initializer_list il, Args&&... args); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_reference_v} is \tcode{false}, and \tcode{is_constructible_v&, Args...>} is \tcode{true}. + +\pnum +\effects +Direct-non-list-initializes \tcode{unex} with \tcode{il, std::forward(args)...}. + +\pnum +\ensures +\tcode{has_value()} is \tcode{false}. + +\pnum +\throws +Any exception thrown by the initialization of \tcode{unex}. + +\pnum +\remarks +An overload with the same parameter types is defined as deleted when \tcode{E} is an lvalue reference type. An initializer list cannot provide the required long-lived error referent. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + requires is_reference_v +constexpr expected(unexpect_t, initializer_list, Args&&...); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\remarks +An overload with the same parameter types is defined as deleted when \tcode{E} is an lvalue reference type. An initializer list cannot provide the required long-lived error referent. +\end{itemdescr} + +\rSec3[expected.object.dtor]{Destructor} + +\indexlibrarydtor{expected}% +\begin{itemdecl} +constexpr ~expected(); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_trivially_destructible_v} is \tcode{true} and \tcode{is_trivially_destructible_v} is \tcode{true}. + +\pnum +\effects +None: \tcode{val} or \tcode{unex} (whichever is active) has a trivial destructor. + +\pnum +\remarks +This destructor is trivial. +\end{itemdescr} + +\indexlibrarydtor{expected}% +\begin{itemdecl} +constexpr ~expected(); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{(is_trivially_destructible_v && is_trivially_destructible_v)} is \tcode{false}. + +\pnum +\effects +If \tcode{has_value()} is \tcode{true}, destroys \tcode{val}, otherwise destroys \tcode{unex}. +\end{itemdescr} + +\rSec3[expected.object.assign]{Assignment} + +\indexlibrarymember{operator=}{expected}% +\begin{itemdecl} +constexpr expected& operator=(const expected&); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\begin{itemize} +\item \tcode{is_trivially_copy_constructible_v} is \tcode{true}, +\item \tcode{is_trivially_copy_assignable_v} is \tcode{true}, +\item \tcode{is_trivially_destructible_v} is \tcode{true}, +\item \tcode{is_trivially_copy_constructible_v} is \tcode{true}, +\item \tcode{is_trivially_copy_assignable_v} is \tcode{true}, +\item \tcode{is_trivially_destructible_v} is \tcode{true}. +\end{itemize} + +\pnum +\effects +Trivially copies \tcode{rhs}'s active member into \tcode{*this}. + +\pnum +\returns +\tcode{*this}. + +\pnum +\remarks +This operator is trivial. +\end{itemdescr} + +\indexlibrarymember{operator=}{expected}% +\begin{itemdecl} +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); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\begin{itemize} +\item \tcode{is_copy_constructible_v} is \tcode{true}, +\item \tcode{is_copy_assignable_v} is \tcode{true}, +\item \tcode{is_reference_v || (is_copy_constructible_v && is_copy_assignable_v)} is \tcode{true}, +\item \tcode{is_nothrow_move_constructible_v || is_nothrow_move_constructible_v} is \tcode{true}, +\item \tcode{(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)} is \tcode{false}. +\end{itemize} + +\pnum +\effects +If \tcode{this->has_value() && rhs.has_value()}, equivalent to \tcode{val = *rhs}. Otherwise, if \tcode{this->has_value()}, equivalent to \tcode{reinit-expected(unex, val, rhs.error())}. Otherwise, if \tcode{rhs.has_value()}, equivalent to \tcode{reinit-expected(val, unex, *rhs)}. Otherwise, equivalent to \tcode{unex = rhs.unex}. Then, if no exception was thrown, equivalent to: \tcode{has_val = rhs.has_value(); return *this;} When \tcode{E} is an lvalue reference type, each of the cases above that initializes or assigns \tcode{unex} rebinds it: \tcode{unex} comes to refer to the same object as \tcode{rhs}'s error. No previously or subsequently referenced object is assigned through. + +\pnum +\returns +\tcode{*this}. + +\pnum +\remarks +This operator is defined as deleted unless \tcode{is_copy_assignable_v} is \tcode{true} and \tcode{is_copy_constructible_v} is \tcode{true} and \tcode{is_copy_assignable_v} is \tcode{true} or \tcode{is_reference_v} is \tcode{true} and \tcode{is_copy_constructible_v} is \tcode{true} or \tcode{is_reference_v} is \tcode{true} and \tcode{is_nothrow_move_constructible_v || is_nothrow_move_constructible_v} is \tcode{true}. This operator is trivial if \tcode{is_trivially_copy_constructible_v}, \tcode{is_trivially_copy_assignable_v}, \tcode{is_trivially_destructible_v}, \tcode{is_trivially_copy_constructible_v}, \tcode{is_trivially_copy_assignable_v}, and \tcode{is_trivially_destructible_v} are all \tcode{true}. +\end{itemdescr} + +\indexlibrarymember{operator=}{expected}% +\begin{itemdecl} +constexpr expected& operator=(expected&&) noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\begin{itemize} +\item \tcode{is_trivially_move_constructible_v} is \tcode{true}, +\item \tcode{is_trivially_move_assignable_v} is \tcode{true}, +\item \tcode{is_trivially_destructible_v} is \tcode{true}, +\item \tcode{is_trivially_move_constructible_v} is \tcode{true}, +\item \tcode{is_trivially_move_assignable_v} is \tcode{true}, +\item \tcode{is_trivially_destructible_v} is \tcode{true}. +\end{itemize} + +\pnum +\effects +Trivially moves \tcode{rhs}'s active member into \tcode{*this}. + +\pnum +\returns +\tcode{*this}. + +\pnum +\remarks +This operator is trivial. +\end{itemdescr} + +\indexlibrarymember{operator=}{expected}% +\begin{itemdecl} +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); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\begin{itemize} +\item \tcode{is_move_constructible_v} is \tcode{true}, +\item \tcode{is_move_assignable_v} is \tcode{true}, +\item \tcode{is_reference_v || (is_move_constructible_v && is_move_assignable_v)} is \tcode{true}, +\item \tcode{is_nothrow_move_constructible_v || is_nothrow_move_constructible_v} is \tcode{true}, +\item \tcode{(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)} is \tcode{false}. +\end{itemize} + +\pnum +\effects +If \tcode{this->has_value() && rhs.has_value()}, equivalent to \tcode{val = std::move(*rhs)}. Otherwise, if \tcode{this->has_value()}, equivalent to \tcode{reinit-expected(unex, val, std::move(rhs.error()))}. Otherwise, if \tcode{rhs.has_value()}, equivalent to \tcode{reinit-expected(val, unex, std::move(*rhs))}. Otherwise, equivalent to \tcode{unex = std::move(rhs.unex)}. Then, if no exception was thrown, equivalent to: \tcode{has_val = rhs.has_value(); return *this;} When \tcode{E} is an lvalue reference type, each of the cases above that initializes or assigns \tcode{unex} rebinds it: \tcode{unex} comes to refer to the same object as \tcode{rhs}'s error. No previously or subsequently referenced object is assigned through. + +\pnum +\returns +\tcode{*this}. + +\pnum +\remarks +The exception specification is equivalent to \tcode{is_nothrow_move_assignable_v && is_nothrow_move_constructible_v && is_nothrow_move_assignable_v && is_nothrow_move_constructible_v}. This operator is trivial if \tcode{is_trivially_move_constructible_v}, \tcode{is_trivially_move_assignable_v}, \tcode{is_trivially_destructible_v}, \tcode{is_trivially_move_constructible_v}, \tcode{is_trivially_move_assignable_v}, and \tcode{is_trivially_destructible_v} are all \tcode{true}. +\end{itemdescr} + +\indexlibrarymember{operator=}{expected}% +\begin{itemdecl} +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); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_same_v>} is \tcode{false}; and \tcode{remove_cvref_t} is not a specialization of \tcode{unexpected}; and \tcode{is_constructible_v} is \tcode{true}; and \tcode{is_assignable_v} is \tcode{true}; and \tcode{is_nothrow_constructible_v || is_nothrow_move_constructible_v || is_nothrow_move_constructible_v} is \tcode{true}. + +\pnum +\effects +If \tcode{has_value()} is \tcode{true}, equivalent to: \tcode{val = std::forward(v);} Otherwise, equivalent to: \tcode{reinit-expected(val, unex, std::forward(v)); has_val = true;} + +\pnum +\returns +\tcode{*this}. +\end{itemdescr} + +\indexlibrarymember{operator=}{expected}% +\begin{itemdecl} +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); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}; and \tcode{is_assignable_v} is \tcode{true}; and \tcode{is_nothrow_constructible_v || is_nothrow_move_constructible_v || is_nothrow_move_constructible_v} is \tcode{true}. + +\pnum +\effects +If \tcode{has_value()} is \tcode{true}, equivalent to: \tcode{reinit-expected(unex, val, e.error()); has_val = false;} Otherwise, equivalent to: \tcode{unex = unexpected(e.error());} + +\pnum +\returns +\tcode{*this}. +\end{itemdescr} + +\indexlibrarymember{operator=}{expected}% +\begin{itemdecl} +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); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_reference_v} is \tcode{true}; and \tcode{is_constructible_v} is \tcode{true}; and \tcode{reference_constructs_from_temporary_v} is \tcode{false}. + +\pnum +\effects +Rebinds \tcode{unex} to refer to the same object as \tcode{e.error()}, destroying \tcode{val} first if \tcode{has_value()} is \tcode{true}. + +\pnum +\ensures +\tcode{has_value()} is \tcode{false}. + +\pnum +\returns +\tcode{*this}. + +\pnum +\remarks +This operator never throws: the referent is bound, not copied. +\end{itemdescr} + +\indexlibrarymember{operator=}{expected}% +\begin{itemdecl} +template + requires(is_reference_v && !is_reference_v) +constexpr expected& operator=(const unexpected&); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\remarks +When \tcode{E} is a reference type, an overload taking \tcode{unexpected} for a non-reference \tcode{G} is defined as deleted: it would rebind \tcode{unex} to \tcode{unexpected}'s temporary storage. +\end{itemdescr} + +\indexlibrarymember{emplace}{expected}% +\begin{itemdecl} +template + requires is_nothrow_constructible_v +constexpr T& emplace(Args&&... args) noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_nothrow_constructible_v} is \tcode{true}. + +\pnum +\effects +Equivalent to: \tcode{if (has_value()) { destroy_at(addressof(val)); } else { destroy_at(addressof(unex)); has_val = true; } return *construct_at(addressof(val), std::forward(args)...);} +\end{itemdescr} + +\indexlibrarymember{emplace}{expected}% +\begin{itemdecl} +template + requires is_nothrow_constructible_v&, Args...> +constexpr T& emplace(initializer_list il, Args&&... args) noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_nothrow_constructible_v&, Args...>} is \tcode{true}. + +\pnum +\effects +Equivalent to: \tcode{if (has_value()) { destroy_at(addressof(val)); } else { destroy_at(addressof(unex)); has_val = true; } return *construct_at(addressof(val), il, std::forward(args)...);} +\end{itemdescr} + +\rSec3[expected.object.swap]{Swap} + +\indexlibrarymember{swap}{expected}% +\begin{itemdecl} +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)); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\begin{itemize} +\item \tcode{is_swappable_v} is \tcode{true}, +\item \tcode{is_reference_v || is_swappable_v} is \tcode{true}, +\item \tcode{is_move_constructible_v} is \tcode{true}, +\item \tcode{is_move_constructible_v} is \tcode{true}, +\item \tcode{is_nothrow_move_constructible_v || is_nothrow_move_constructible_v} is \tcode{true}. +\end{itemize} + +\pnum +\effects +If \tcode{this->has_value()} and \tcode{rhs.has_value()}, equivalent to \tcode{using std::swap; swap(val, rhs.val);}. If neither \tcode{*this} nor \tcode{rhs} contains a value, equivalent to \tcode{using std::swap; swap(unex, rhs.unex);}. If \tcode{rhs.has_value()} is \tcode{false} and \tcode{this->has_value()} is \tcode{true}, exchanges the value and error between \tcode{*this} and \tcode{rhs} (moving through a temporary so a failed move leaves both objects unchanged), leaving \tcode{has_value()} \tcode{false} and \tcode{rhs.has_value()} \tcode{true}. If \tcode{rhs.has_value()} is \tcode{true} and \tcode{this->has_value()} is \tcode{false}, equivalent to \tcode{rhs.swap(*this)}. + +\pnum +\throws +Any exception thrown by the expressions in the Effects. + +\pnum +\remarks +The exception specification is equivalent to \tcode{is_nothrow_move_constructible_v && is_nothrow_swappable_v && is_nothrow_move_constructible_v && (is_reference_v || is_nothrow_swappable_v)}. +\end{itemdescr} + +\indexlibraryglobal{swap}% +\begin{itemdecl} +friend constexpr void swap(expected& x, expected& y) noexcept(noexcept(x.swap(y))); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\begin{itemize} +\item \tcode{is_swappable_v} is \tcode{true}, +\item \tcode{is_reference_v || is_swappable_v} is \tcode{true}, +\item \tcode{is_move_constructible_v} is \tcode{true}, +\item \tcode{is_move_constructible_v} is \tcode{true}, +\item \tcode{is_nothrow_move_constructible_v || is_nothrow_move_constructible_v} is \tcode{true}. +\end{itemize} + +\pnum +\effects +Equivalent to \tcode{x.swap(y)}. +\end{itemdescr} + +\rSec3[expected.object.obs]{Observers} + +\indexlibrarymember{operator->}{expected}% +\begin{itemdecl} +constexpr const T* operator->() const noexcept; +constexpr T* operator->() noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\hardexpects +\tcode{has_value()} is \tcode{true}. + +\pnum +\returns +\tcode{addressof(val)}. +\end{itemdescr} + +\indexlibrarymember{operator*}{expected}% +\begin{itemdecl} +constexpr const T& operator*() const& noexcept; +constexpr T& operator*() & noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\hardexpects +\tcode{has_value()} is \tcode{true}. + +\pnum +\returns +\tcode{val}. +\end{itemdescr} + +\indexlibrarymember{operator*}{expected}% +\begin{itemdecl} +constexpr const T&& operator*() const&& noexcept; +constexpr T&& operator*() && noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\hardexpects +\tcode{has_value()} is \tcode{true}. + +\pnum +\returns +\tcode{std::move(val)}. +\end{itemdescr} + +\indexlibrarymember{operator bool}{expected}% +\indexlibrarymember{has_value}{expected}% +\begin{itemdecl} +constexpr explicit operator bool() const noexcept; +constexpr bool has_value() const noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\tcode{has_val}. +\end{itemdescr} + +\indexlibrarymember{value}{expected}% +\begin{itemdecl} +constexpr const T& value() const&; +constexpr T& value() &; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{is_copy_constructible_v} is \tcode{true}. + +\pnum +\returns +\tcode{val}, if \tcode{has_value()} is \tcode{true}. + +\pnum +\throws +\tcode{bad_expected_access(as_const(error()))} if \tcode{has_value()} is \tcode{false}. +\end{itemdescr} + +\indexlibrarymember{value}{expected}% +\begin{itemdecl} +constexpr const T&& value() const&&; +constexpr T&& value() &&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{is_copy_constructible_v} is \tcode{true} and \tcode{is_constructible_v} is \tcode{true}. + +\pnum +\returns +\tcode{std::move(val)}, if \tcode{has_value()} is \tcode{true}. + +\pnum +\throws +\tcode{bad_expected_access(std::move(error()))} if \tcode{has_value()} is \tcode{false}. +\end{itemdescr} + +\indexlibrarymember{error}{expected}% +\begin{itemdecl} +constexpr const E& error() const& noexcept; +constexpr E& error() & noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\hardexpects +\tcode{has_value()} is \tcode{false}. + +\pnum +\returns +\tcode{unex.error()}. +\end{itemdescr} + +\indexlibrarymember{error}{expected}% +\begin{itemdecl} +constexpr const E&& error() const&& noexcept; +constexpr E&& error() && noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\hardexpects +\tcode{has_value()} is \tcode{false}. + +\pnum +\returns +\tcode{std::move(unex).error()}. +\end{itemdescr} + +\indexlibrarymember{value_or}{expected}% +\begin{itemdecl} +template> constexpr T value_or(U&& def) const&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{is_copy_constructible_v} is \tcode{true} and \tcode{is_convertible_v} is \tcode{true}. + +\pnum +\returns +\tcode{has_value() ? **this : static_cast(std::forward(def))}. +\end{itemdescr} + +\indexlibrarymember{value_or}{expected}% +\begin{itemdecl} +template> constexpr T value_or(U&& def) &&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{is_move_constructible_v} is \tcode{true} and \tcode{is_convertible_v} is \tcode{true}. + +\pnum +\returns +\tcode{has_value() ? std::move(**this) : static_cast(std::forward(def))}. +\end{itemdescr} + +\indexlibrarymember{error_or}{expected}% +\begin{itemdecl} +template + requires(is_copy_constructible_v<@\exposidnc{error-value-type}@> && + is_convertible_v) +constexpr @\exposidnc{error-value-type}@ error_or(G&& def) const&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{is_copy_constructible_v} is \tcode{true} and \tcode{is_convertible_v} is \tcode{true}. + +\pnum +\returns +\tcode{std::forward(def)} if \tcode{has_value()} is \tcode{true}, \tcode{error()} otherwise. +\end{itemdescr} + +\indexlibrarymember{error_or}{expected}% +\begin{itemdecl} +template + requires(is_move_constructible_v<@\exposidnc{error-value-type}@> && + is_convertible_v) +constexpr @\exposidnc{error-value-type}@ error_or(G&& def) &&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{is_move_constructible_v} is \tcode{true} and \tcode{is_convertible_v} is \tcode{true}. + +\pnum +\returns +\tcode{std::forward(def)} if \tcode{has_value()} is \tcode{true}, \tcode{std::move(error())} otherwise. +\end{itemdescr} + +\rSec3[expected.object.monadic]{Monadic operations} + +\indexlibrarymember{and_then}{expected}% +\begin{itemdecl} +template + requires is_constructible_v +constexpr auto and_then(F&& f) &; +template + requires is_constructible_v +constexpr auto and_then(F&& f) const&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\mandates +\tcode{remove_cvref_t>} is a specialization of \tcode{expected} and its \tcode{error_type} is the same type as \tcode{E}. + +\pnum +\effects +Equivalent to: \tcode{if (has_value()) return invoke(std::forward(f), val); else return U(unexpect, error());} where \tcode{U} is \tcode{remove_cvref_t>}. +\end{itemdescr} + +\indexlibrarymember{and_then}{expected}% +\begin{itemdecl} +template + requires is_constructible_v +constexpr auto and_then(F&& f) &&; +template + requires is_constructible_v +constexpr auto and_then(F&& f) const&&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\mandates +\tcode{remove_cvref_t>} is a specialization of \tcode{expected} and its \tcode{error_type} is the same type as \tcode{E}. + +\pnum +\effects +Equivalent to: \tcode{if (has_value()) return invoke(std::forward(f), std::move(val)); else return U(unexpect, std::move(error()));} where \tcode{U} is \tcode{remove_cvref_t>}. +\end{itemdescr} + +\indexlibrarymember{or_else}{expected}% +\begin{itemdecl} +template + requires is_constructible_v +constexpr auto or_else(F&& f) &; +template + requires is_constructible_v +constexpr auto or_else(F&& f) const&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\mandates +\tcode{remove_cvref_t>} is a specialization of \tcode{expected} and its \tcode{value_type} is the same type as \tcode{T}. + +\pnum +\effects +Equivalent to: \tcode{if (has_value()) return G(in_place, val); else return invoke(std::forward(f), error());} where \tcode{G} is \tcode{remove_cvref_t>}. +\end{itemdescr} + +\indexlibrarymember{or_else}{expected}% +\begin{itemdecl} +template + requires is_constructible_v +constexpr auto or_else(F&& f) &&; +template + requires is_constructible_v +constexpr auto or_else(F&& f) const&&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\mandates +\tcode{remove_cvref_t>} is a specialization of \tcode{expected} and its \tcode{value_type} is the same type as \tcode{T}. + +\pnum +\effects +Equivalent to: \tcode{if (has_value()) return G(in_place, std::move(val)); else return invoke(std::forward(f), std::move(error()));} where \tcode{G} is \tcode{remove_cvref_t>}. +\end{itemdescr} + +\indexlibrarymember{transform}{expected}% +\begin{itemdecl} +template + requires is_constructible_v +constexpr auto transform(F&& f) &; +template + requires is_constructible_v +constexpr auto transform(F&& f) const&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\effects +Equivalent to: \tcode{if (!has_value()) return U(unexpect, error()); else return expected(in_place, invoke(std::forward(f), val));} where \tcode{U2} is \tcode{remove_cv_t>} and \tcode{U} is \tcode{expected}. +\end{itemdescr} + +\indexlibrarymember{transform}{expected}% +\begin{itemdecl} +template + requires is_constructible_v +constexpr auto transform(F&& f) &&; +template + requires is_constructible_v +constexpr auto transform(F&& f) const&&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\effects +Equivalent to: \tcode{if (!has_value()) return U(unexpect, std::move(error())); else return expected(in_place, invoke(std::forward(f), std::move(val)));} where \tcode{U2} is \tcode{remove_cv_t>} and \tcode{U} is \tcode{expected}. +\end{itemdescr} + +\indexlibrarymember{transform_error}{expected}% +\begin{itemdecl} +template + requires is_constructible_v +constexpr auto transform_error(F&& f) &; +template + requires is_constructible_v +constexpr auto transform_error(F&& f) const&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\mandates +\begin{itemize} +\item \tcode{is_object_v} is \tcode{true}, +\item \tcode{is_array_v} is \tcode{false}, +\item \tcode{is_same_v>} is \tcode{true}, +\item \tcode{\exposid{is-unexpected-specialization}::value} is \tcode{false}. +\end{itemize} + +\pnum +\effects +Equivalent to: \tcode{if (has_value()) return G(in_place, val); else return expected(unexpect, invoke(std::forward(f), error()));} where \tcode{G2} is \tcode{remove_cv_t>} and \tcode{G} is \tcode{expected}. +\end{itemdescr} + +\indexlibrarymember{transform_error}{expected}% +\begin{itemdecl} +template + requires is_constructible_v +constexpr auto transform_error(F&& f) &&; +template + requires is_constructible_v +constexpr auto transform_error(F&& f) const&&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\mandates +\begin{itemize} +\item \tcode{is_object_v} is \tcode{true}, +\item \tcode{is_array_v} is \tcode{false}, +\item \tcode{is_same_v>} is \tcode{true}, +\item \tcode{\exposid{is-unexpected-specialization}::value} is \tcode{false}. +\end{itemize} + +\pnum +\effects +Equivalent to: \tcode{if (has_value()) return G(in_place, std::move(val)); else return expected(unexpect, invoke(std::forward(f), std::move(error())));} where \tcode{G2} is \tcode{remove_cv_t>} and \tcode{G} is \tcode{expected}. +\end{itemdescr} + +\rSec3[expected.object.eq]{Equality operators} + +\indexlibraryglobal{operator==}% +\begin{itemdecl} +template + requires(!is_void_v) +friend constexpr bool operator==(const expected& x, const expected& y); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{!is_void_v} is \tcode{true}. The expression \tcode{*x == *y} is well-formed and its result is convertible to \tcode{bool}. The expression \tcode{x.error() == y.error()} is well-formed and its result is convertible to \tcode{bool}. + +\pnum +\returns +If \tcode{x.has_value() != y.has_value()}, \tcode{false}; otherwise, if \tcode{x.has_value()} is \tcode{true}, \tcode{*x == *y}; otherwise \tcode{x.error() == y.error()}. +\end{itemdescr} + +\indexlibraryglobal{operator==}% +\begin{itemdecl} +template + requires(!@\exposidnc{is-expected-specialization}@::value) +friend constexpr bool operator==(const expected& x, const T2& val); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{T2} is not a specialization of \tcode{expected}. The expression \tcode{*x == val} is well-formed and its result is convertible to \tcode{bool}. + +\pnum +\returns +\tcode{x.has_value() && static_cast(*x == val)}. +\end{itemdescr} + +\indexlibraryglobal{operator==}% +\begin{itemdecl} +template +friend constexpr bool operator==(const expected& x, const unexpected& e); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +The expression \tcode{x.error() == e.error()} is well-formed and its result is convertible to \tcode{bool}. + +\pnum +\returns +\tcode{!x.has_value() && static_cast(x.error() == e.error())}. +\end{itemdescr} + +\rSec2[expected.void]{Partial specialization of expected for void types} + +\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 + }; +}; +\end{codeblock} + +\begin{itemdescr} +\pnum +\mandates +A program that instantiates the definition of \tcode{expected} with an \tcode{E} that is not a valid template argument for \tcode{unexpected} is ill-formed. + +\pnum +\remarks +Any object of type \tcode{expected} either represents a value of type \tcode{T}, or contains a value of type \tcode{E} nested within it. Member \tcode{has_val} indicates whether the \tcode{expected} object represents a value of type \tcode{T}. When \tcode{has_value()} is \tcode{false}, the error is \tcode{unex.error()}. +\end{itemdescr} + +\rSec3[expected.void.cons]{Constructors} + +\indexlibraryctor{expected}% +\begin{itemdecl} +constexpr expected() noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\ensures +\tcode{has_value()} is \tcode{true}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +constexpr expected(const expected& rhs) noexcept(is_nothrow_copy_constructible_v); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_copy_constructible_v} is \tcode{true} and \tcode{is_trivially_copy_constructible_v} is \tcode{false}. + +\pnum +\effects +If \tcode{rhs.has_value()} is \tcode{false}, direct-non-list-initializes \tcode{unex} with \tcode{rhs.error()}. + +\pnum +\ensures +\tcode{rhs.has_value() == this->has_value()}. + +\pnum +\throws +Any exception thrown by the initialization of \tcode{unex}. + +\pnum +\remarks +This constructor is defined as deleted unless \tcode{is_copy_constructible_v} is \tcode{true} or \tcode{is_reference_v} is \tcode{true}. This constructor is trivial if \tcode{is_trivially_copy_constructible_v} is \tcode{true}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +constexpr expected(expected&& rhs) noexcept(is_nothrow_move_constructible_v); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_move_constructible_v} is \tcode{true} and \tcode{is_trivially_move_constructible_v} is \tcode{false}. + +\pnum +\effects +If \tcode{rhs.has_value()} is \tcode{false}, direct-non-list-initializes \tcode{unex} with \tcode{std::move(rhs.error())}. + +\pnum +\ensures +\tcode{rhs.has_value()} is unchanged; \tcode{rhs.has_value() == this->has_value()} is \tcode{true}. + +\pnum +\throws +Any exception thrown by the initialization of \tcode{unex}. + +\pnum +\remarks +This constructor is trivial if \tcode{is_trivially_move_constructible_v} is \tcode{true}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +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); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_void_v} is \tcode{true}; and \tcode{is_constructible_v} is \tcode{true}; and \tcode{is_constructible_v, expected&>} is \tcode{false}; and \tcode{is_constructible_v, expected>} is \tcode{false}; and \tcode{is_constructible_v, const expected&>} is \tcode{false}; and \tcode{is_constructible_v, const expected>} is \tcode{false}. + +\pnum +\effects +If \tcode{rhs.has_value()} is \tcode{false}, direct-non-list-initializes \tcode{unex} with \tcode{rhs.error()}. + +\pnum +\ensures +\tcode{rhs.has_value()} is unchanged; \tcode{rhs.has_value() == this->has_value()} is \tcode{true}. + +\pnum +\throws +Any exception thrown by the initialization of \tcode{unex}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +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); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\effects +Direct-non-list-initializes \tcode{unex} with \tcode{e.error()}. + +\pnum +\ensures +\tcode{has_value()} is \tcode{false}. + +\pnum +\throws +Any exception thrown by the initialization of \tcode{unex}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +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; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_reference_v} is \tcode{true}; and \tcode{is_constructible_v} is \tcode{true}; and \tcode{reference_constructs_from_temporary_v} is \tcode{false}. + +\pnum +\effects +Initializes \tcode{unex} with \tcode{e.error()}. + +\pnum +\ensures +\tcode{has_value()} is \tcode{false}. + +\pnum +\remarks +This constructor never throws: the referent is bound, not copied. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + requires(is_reference_v && !is_reference_v) +constexpr expected(const unexpected&); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\remarks +When \tcode{E} is a reference type, an overload taking \tcode{unexpected} for a non-reference \tcode{G} is defined as deleted: the referent would live inside the (possibly temporary) source \tcode{unexpected} object, and binding \tcode{E&} to it would dangle. Use an \tcode{unexpected} holding an external object instead. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +constexpr explicit expected(in_place_t) noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\ensures +\tcode{has_value()} is \tcode{true}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + requires(is_constructible_v && !@\exposidnc{unexpect-dangles-v}@) +constexpr explicit expected(unexpect_t, Args&&... args); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\effects +Direct-non-list-initializes \tcode{unex} with \tcode{std::forward(args)...}. + +\pnum +\ensures +\tcode{has_value()} is \tcode{false}. + +\pnum +\throws +Any exception thrown by the initialization of \tcode{unex}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + requires(@\exposidnc{unexpect-dangles-v}@) +constexpr expected(unexpect_t, Args&&...); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\remarks +When \tcode{E} is a reference type, an overload with the same parameter types is defined as deleted if the single argument would bind \tcode{E&} to a temporary, or if it is otherwise not usable to construct \tcode{E}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + requires(!is_reference_v && is_constructible_v&, Args...>) +constexpr explicit expected(unexpect_t, initializer_list il, Args&&... args); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_reference_v} is \tcode{false}, and \tcode{is_constructible_v&, Args...>} is \tcode{true}. + +\pnum +\effects +Direct-non-list-initializes \tcode{unex} with \tcode{il, std::forward(args)...}. + +\pnum +\ensures +\tcode{has_value()} is \tcode{false}. + +\pnum +\throws +Any exception thrown by the initialization of \tcode{unex}. + +\pnum +\remarks +An overload with the same parameter types is defined as deleted when \tcode{E} is an lvalue reference type. An initializer list cannot provide the required long-lived error referent. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + requires is_reference_v +constexpr expected(unexpect_t, initializer_list, Args&&...); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\remarks +An overload with the same parameter types is defined as deleted when \tcode{E} is an lvalue reference type. An initializer list cannot provide the required long-lived error referent. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +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); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_convertible_v} is \tcode{true}; and \tcode{reference_constructs_from_temporary_v} is \tcode{false}. + +\pnum +\effects +If \tcode{rhs.has_value()} is \tcode{false}, direct-non-list-initializes \tcode{unex} with \tcode{rhs.error()}. + +\pnum +\ensures +\tcode{rhs.has_value()} is unchanged; \tcode{rhs.has_value() == this->has_value()} is \tcode{true}. + +\pnum +\remarks +This constructor never throws: the referent is bound, not copied. It participates in overload resolution only when \tcode{E} is a reference type, mirroring the \tcode{unexpected} reference-\tcode{E} path above. +\end{itemdescr} + +\rSec3[expected.void.dtor]{Destructor} + +\indexlibrarydtor{expected}% +\begin{itemdecl} +constexpr ~expected(); +\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}. + +\pnum +\remarks +If \tcode{is_trivially_destructible_v} is \tcode{true}, then this destructor is a trivial destructor. +\end{itemdescr} + +\rSec3[expected.void.assign]{Assignment} + +\indexlibrarymember{operator=}{expected}% +\begin{itemdecl} +constexpr expected& operator=(const expected& rhs) noexcept( + is_nothrow_copy_constructible_v && is_nothrow_copy_assignable_v); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_reference_v || (is_copy_constructible_v && is_copy_assignable_v)} is \tcode{true} and \tcode{(is_trivially_copy_constructible_v && is_trivially_copy_assignable_v && + is_trivially_destructible_v)} is \tcode{false}. + +\pnum +\effects +If \tcode{this->has_value() && rhs.has_value()}, no effects. Otherwise, if \tcode{this->has_value()}, equivalent to: \tcode{construct_at(addressof(unex), rhs.unex); has_val = false;} Otherwise, if \tcode{rhs.has_value()}, destroys \tcode{unex} and sets \tcode{has_val} to \tcode{true}. Otherwise, equivalent to \tcode{unex = rhs.unex}. + +\pnum +\returns +\tcode{*this}. + +\pnum +\remarks +This operator is defined as deleted unless \tcode{is_copy_assignable_v} is \tcode{true} or \tcode{is_reference_v} is \tcode{true} and \tcode{is_copy_constructible_v} is \tcode{true} or \tcode{is_reference_v} is \tcode{true}. This operator is trivial if \tcode{is_trivially_copy_constructible_v}, \tcode{is_trivially_copy_assignable_v}, and \tcode{is_trivially_destructible_v} are all \tcode{true}. +\end{itemdescr} + +\indexlibrarymember{operator=}{expected}% +\begin{itemdecl} +constexpr expected& operator=(expected&& rhs) noexcept( + is_nothrow_move_constructible_v && is_nothrow_move_assignable_v); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_reference_v || (is_move_constructible_v && is_move_assignable_v)} is \tcode{true} and \tcode{(is_trivially_move_constructible_v && is_trivially_move_assignable_v && + is_trivially_destructible_v)} is \tcode{false}. + +\pnum +\effects +If \tcode{this->has_value() && rhs.has_value()}, no effects. Otherwise, if \tcode{this->has_value()}, equivalent to: \tcode{construct_at(addressof(unex), std::move(rhs.unex)); has_val = false;} Otherwise, if \tcode{rhs.has_value()}, destroys \tcode{unex} and sets \tcode{has_val} to \tcode{true}. Otherwise, equivalent to \tcode{unex = std::move(rhs.unex)}. + +\pnum +\returns +\tcode{*this}. + +\pnum +\remarks +The exception specification is equivalent to \tcode{is_nothrow_move_constructible_v && is_nothrow_move_assignable_v}. This operator is trivial if \tcode{is_trivially_move_constructible_v}, \tcode{is_trivially_move_assignable_v}, and \tcode{is_trivially_destructible_v} are all \tcode{true}. +\end{itemdescr} + +\indexlibrarymember{operator=}{expected}% +\begin{itemdecl} +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); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true} and \tcode{is_assignable_v} is \tcode{true}. + +\pnum +\effects +If \tcode{has_value()} is \tcode{true}, equivalent to: \tcode{construct_at(addressof(unex), e.error()); has_val = false;} Otherwise, equivalent to: \tcode{unex = unexpected(e.error());} + +\pnum +\returns +\tcode{*this}. +\end{itemdescr} + +\indexlibrarymember{operator=}{expected}% +\begin{itemdecl} +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); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_reference_v} is \tcode{true}; and \tcode{is_constructible_v} is \tcode{true}; and \tcode{reference_constructs_from_temporary_v} is \tcode{false}. + +\pnum +\effects +Rebinds \tcode{unex} to refer to the same object as \tcode{e.error()}. + +\pnum +\ensures +\tcode{has_value()} is \tcode{false}. + +\pnum +\returns +\tcode{*this}. + +\pnum +\remarks +This operator never throws: the referent is bound, not copied. +\end{itemdescr} + +\indexlibrarymember{operator=}{expected}% +\begin{itemdecl} +template + requires(is_reference_v && !is_reference_v) +constexpr expected& operator=(const unexpected&); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\remarks +When \tcode{E} is a reference type, an overload taking \tcode{unexpected} for a non-reference \tcode{G} is defined as deleted: it would rebind \tcode{unex} to \tcode{unexpected}'s temporary storage. +\end{itemdescr} + +\indexlibrarymember{emplace}{expected}% +\begin{itemdecl} +constexpr void emplace() noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +If \tcode{has_value()} is \tcode{false}, destroys \tcode{unex} and sets \tcode{has_val} to \tcode{true}. +\end{itemdescr} + +\rSec3[expected.void.swap]{Swap} + +\indexlibrarymember{swap}{expected}% +\begin{itemdecl} +constexpr void swap(expected& rhs) noexcept(is_nothrow_move_constructible_v && + (is_reference_v || + is_nothrow_swappable_v)); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_reference_v || is_swappable_v} is \tcode{true} and \tcode{is_move_constructible_v} is \tcode{true}. + +\pnum +\effects +If \tcode{this->has_value()} and \tcode{rhs.has_value()}, no effects. If neither \tcode{*this} nor \tcode{rhs} contains a value, equivalent to \tcode{using std::swap; swap(unex, rhs.unex);}. If \tcode{rhs.has_value()} is \tcode{false} and \tcode{this->has_value()} is \tcode{true}, initializes \tcode{rhs.unex} from \tcode{std::move(unex)}, destroys \tcode{unex}, and leaves \tcode{has_value()} \tcode{false} and \tcode{rhs.has_value()} \tcode{true}. If \tcode{rhs.has_value()} is \tcode{true} and \tcode{this->has_value()} is \tcode{false}, equivalent to \tcode{rhs.swap(*this)}. + +\pnum +\throws +Any exception thrown by the expressions in the Effects. + +\pnum +\remarks +The exception specification is equivalent to \tcode{is_nothrow_move_constructible_v && (is_reference_v || is_nothrow_swappable_v)}. +\end{itemdescr} + +\indexlibraryglobal{swap}% +\begin{itemdecl} +friend constexpr void swap(expected& x, expected& y) noexcept(noexcept(x.swap(y))); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_reference_v || is_swappable_v} is \tcode{true} and \tcode{is_move_constructible_v} is \tcode{true}. + +\pnum +\effects +Equivalent to \tcode{x.swap(y)}. +\end{itemdescr} + +\rSec3[expected.void.obs]{Observers} + +\indexlibrarymember{operator bool}{expected}% +\indexlibrarymember{has_value}{expected}% +\begin{itemdecl} +constexpr explicit operator bool() const noexcept; +constexpr bool has_value() const noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\tcode{has_val}. +\end{itemdescr} + +\indexlibrarymember{operator*}{expected}% +\begin{itemdecl} +constexpr void operator*() const noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\hardexpects +\tcode{has_value()} is \tcode{true}. +\end{itemdescr} + +\indexlibrarymember{value}{expected}% +\begin{itemdecl} +constexpr void value() const&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{is_copy_constructible_v} is \tcode{true}. + +\pnum +\throws +\tcode{bad_expected_access(error())} if \tcode{has_value()} is \tcode{false}. +\end{itemdescr} + +\indexlibrarymember{value}{expected}% +\begin{itemdecl} +constexpr void value() &&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{is_copy_constructible_v} is \tcode{true} and \tcode{is_move_constructible_v} is \tcode{true}. + +\pnum +\throws +\tcode{bad_expected_access(std::move(error()))} if \tcode{has_value()} is \tcode{false}. +\end{itemdescr} + +\indexlibrarymember{error}{expected}% +\begin{itemdecl} +constexpr const E& error() const& noexcept; +constexpr E& error() & noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\hardexpects +\tcode{has_value()} is \tcode{false}. + +\pnum +\returns +\tcode{unex.error()}. +\end{itemdescr} + +\indexlibrarymember{error}{expected}% +\begin{itemdecl} +constexpr const E&& error() const&& noexcept; +constexpr E&& error() && noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\hardexpects +\tcode{has_value()} is \tcode{false}. + +\pnum +\returns +\tcode{std::move(unex).error()}. +\end{itemdescr} + +\indexlibrarymember{error_or}{expected}% +\begin{itemdecl} +template + requires(is_copy_constructible_v>> && + is_convertible_v>>) +constexpr @\exposidnc{error-value-type}@ error_or(G&& def) const&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{is_copy_constructible_v} is \tcode{true} and \tcode{is_convertible_v} is \tcode{true}. + +\pnum +\returns +\tcode{std::forward(def)} if \tcode{has_value()} is \tcode{true}, \tcode{error()} otherwise. +\end{itemdescr} + +\indexlibrarymember{error_or}{expected}% +\begin{itemdecl} +template + requires(is_move_constructible_v>> && + is_convertible_v>>) +constexpr @\exposidnc{error-value-type}@ error_or(G&& def) &&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{is_move_constructible_v} is \tcode{true} and \tcode{is_convertible_v} is \tcode{true}. + +\pnum +\returns +\tcode{std::forward(def)} if \tcode{has_value()} is \tcode{true}, \tcode{std::move(error())} otherwise. +\end{itemdescr} + +\indexlibrarymember{value_or}{expected}% +\begin{itemdecl} +template + requires is_reference_v +constexpr void value_or(U&&) const; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\remarks +\tcode{expected} has no \tcode{value_or} member: there is no value to fall back from. This overload exists only to give a clear diagnostic when \tcode{E} is a reference type, and is defined as deleted. +\end{itemdescr} + +\rSec3[expected.void.monadic]{Monadic operations} + +\indexlibrarymember{and_then}{expected}% +\begin{itemdecl} +template + requires is_constructible_v +constexpr auto and_then(F&& f) &; +template + requires is_constructible_v +constexpr auto and_then(F&& f) const&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\mandates +\tcode{remove_cvref_t>} is a specialization of \tcode{expected} and its \tcode{error_type} is the same type as \tcode{E}. + +\pnum +\effects +Equivalent to: \tcode{if (has_value()) return invoke(std::forward(f)); else return U(unexpect, error());} where \tcode{U} is \tcode{remove_cvref_t>}. +\end{itemdescr} + +\indexlibrarymember{and_then}{expected}% +\begin{itemdecl} +template + requires is_constructible_v +constexpr auto and_then(F&& f) &&; +template + requires is_constructible_v +constexpr auto and_then(F&& f) const&&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\mandates +\tcode{remove_cvref_t>} is a specialization of \tcode{expected} and its \tcode{error_type} is the same type as \tcode{E}. + +\pnum +\effects +Equivalent to: \tcode{if (has_value()) return invoke(std::forward(f)); else return U(unexpect, std::move(error()));} where \tcode{U} is \tcode{remove_cvref_t>}. +\end{itemdescr} + +\indexlibrarymember{or_else}{expected}% +\begin{itemdecl} +template constexpr auto or_else(F&& f) &; +template constexpr auto or_else(F&& f) const&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{remove_cvref_t>} is a specialization of \tcode{expected} and its \tcode{value_type} is the same type as \tcode{T}. + +\pnum +\effects +Equivalent to: \tcode{if (has_value()) return G(); else return invoke(std::forward(f), error());} where \tcode{G} is \tcode{remove_cvref_t>}. +\end{itemdescr} + +\indexlibrarymember{or_else}{expected}% +\begin{itemdecl} +template constexpr auto or_else(F&& f) &&; +template constexpr auto or_else(F&& f) const&&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{remove_cvref_t>} is a specialization of \tcode{expected} and its \tcode{value_type} is the same type as \tcode{T}. + +\pnum +\effects +Equivalent to: \tcode{if (has_value()) return G(); else return invoke(std::forward(f), std::move(error()));} where \tcode{G} is \tcode{remove_cvref_t>}. +\end{itemdescr} + +\indexlibrarymember{transform}{expected}% +\begin{itemdecl} +template + requires is_constructible_v +constexpr auto transform(F&& f) &; +template + requires is_constructible_v +constexpr auto transform(F&& f) const&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\mandates +\tcode{U} is a valid value type for \tcode{expected}, where \tcode{U} is \tcode{remove_cv_t>}. + +\pnum +\effects +If \tcode{has_value()} is \tcode{false}, returns \tcode{expected(unexpect, error())}. Otherwise, if \tcode{is_void_v} is \tcode{false}, returns an \tcode{expected} object whose \tcode{has_val} member is \tcode{true} and \tcode{val} member is direct-non-list-initialized with \tcode{invoke(std::forward(f))}. Otherwise, evaluates \tcode{invoke(std::forward(f))} and then returns \tcode{expected()}. +\end{itemdescr} + +\indexlibrarymember{transform}{expected}% +\begin{itemdecl} +template + requires is_constructible_v +constexpr auto transform(F&& f) &&; +template + requires is_constructible_v +constexpr auto transform(F&& f) const&&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\mandates +\tcode{U} is a valid value type for \tcode{expected}, where \tcode{U} is \tcode{remove_cv_t>}. + +\pnum +\effects +If \tcode{has_value()} is \tcode{false}, returns \tcode{expected(unexpect, std::move(error()))}. Otherwise, if \tcode{is_void_v} is \tcode{false}, returns an \tcode{expected} object whose \tcode{has_val} member is \tcode{true} and \tcode{val} member is direct-non-list-initialized with \tcode{invoke(std::forward(f))}. Otherwise, evaluates \tcode{invoke(std::forward(f))} and then returns \tcode{expected()}. +\end{itemdescr} + +\indexlibrarymember{transform_error}{expected}% +\begin{itemdecl} +template constexpr auto transform_error(F&& f) &; +template constexpr auto transform_error(F&& f) const&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{G} is a valid template argument for \tcode{unexpected} and the declaration \tcode{G g(invoke(std::forward(f), error()));} is well-formed, where \tcode{G} is \tcode{remove_cv_t>}. + +\pnum +\returns +If \tcode{has_value()} is \tcode{true}, \tcode{expected()}; otherwise, an \tcode{expected} object whose \tcode{has_val} member is \tcode{false} and \tcode{unex} member is direct-non-list-initialized with \tcode{invoke(std::forward(f), error())}. +\end{itemdescr} + +\indexlibrarymember{transform_error}{expected}% +\begin{itemdecl} +template constexpr auto transform_error(F&& f) &&; +template constexpr auto transform_error(F&& f) const&&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{G} is a valid template argument for \tcode{unexpected} and the declaration \tcode{G g(invoke(std::forward(f), std::move(error())));} is well-formed, where \tcode{G} is \tcode{remove_cv_t>}. + +\pnum +\returns +If \tcode{has_value()} is \tcode{true}, \tcode{expected()}; otherwise, an \tcode{expected} object whose \tcode{has_val} member is \tcode{false} and \tcode{unex} member is direct-non-list-initialized with \tcode{invoke(std::forward(f), std::move(error()))}. +\end{itemdescr} + +\rSec3[expected.void.eq]{Equality operators} + +\indexlibraryglobal{operator==}% +\begin{itemdecl} +template + requires is_void_v +friend constexpr bool operator==(const expected& x, const expected& y); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{is_void_v} is \tcode{true}. The expression \tcode{x.error() == y.error()} is well-formed and its result is convertible to \tcode{bool}. + +\pnum +\returns +If \tcode{x.has_value() != y.has_value()}, \tcode{false}; otherwise, if \tcode{x.has_value()} is \tcode{true}, \tcode{true}; otherwise \tcode{x.error() == y.error()}. +\end{itemdescr} + +\indexlibraryglobal{operator==}% +\begin{itemdecl} +template +friend constexpr bool operator==(const expected& x, const unexpected& e); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +The expression \tcode{x.error() == e.error()} is well-formed and its result is convertible to \tcode{bool}. + +\pnum +\returns +\tcode{!x.has_value() && static_cast(x.error() == e.error())}. +\end{itemdescr} + +\rSec2[expected.ref]{Partial specialization of expected for reference types} + +\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 + }; +}; +\end{codeblock} + +\begin{itemdescr} +\pnum +\mandates +A program that instantiates the definition of \tcode{expected} with an \tcode{E} that is not a valid template argument for \tcode{unexpected} is ill-formed. \tcode{T} shall be an object type that is not an array type. + +\pnum +\remarks +An object of type \tcode{expected} either represents a reference to an object of type \tcode{T}, or holds an error. Member \tcode{has_val} indicates whether the object represents a reference. When it represents a reference, member \tcode{val} points to the referenced object, which is not owned by the \tcode{expected} object. Otherwise, the error is \tcode{unex.error()}. +\end{itemdescr} + +\rSec3[expected.ref.cons]{Constructors} + +\indexlibraryctor{expected}% +\begin{itemdecl} +; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\remarks +\tcode{expected} has no default constructor: a reference cannot be null, so there is no empty state to default-construct into. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +constexpr expected(const expected&) = default; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +If \tcode{rhs.has_value()} is \tcode{true}, initializes \tcode{val} with \tcode{rhs.val}, so that \tcode{*this} and \tcode{rhs} refer to the same object; otherwise, initializes \tcode{unex} with \tcode{rhs.unex}. + +\pnum +\ensures +\tcode{rhs.has_value() == this->has_value()}. + +\pnum +\remarks +This constructor is trivial. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +constexpr expected(const expected& rhs) noexcept(is_nothrow_copy_constructible_v); +constexpr expected(expected&& rhs) noexcept(is_nothrow_move_constructible_v); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_copy_constructible_v} is \tcode{true} and \tcode{is_trivially_copy_constructible_v} is \tcode{false}. + +\pnum +\effects +If \tcode{rhs.has_value()} is \tcode{true}, initializes \tcode{val} with \tcode{rhs.val}, so that \tcode{*this} and \tcode{rhs} refer to the same object; otherwise, initializes \tcode{unex} with \tcode{rhs.unex}. + +\pnum +\ensures +\tcode{rhs.has_value() == this->has_value()}. + +\pnum +\remarks +This constructor is trivial if the corresponding constructor of \tcode{E} is trivial, and is defined as deleted unless \tcode{is_copy_constructible_v} is \tcode{true}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +constexpr expected(expected&&) = default; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +If \tcode{rhs.has_value()} is \tcode{true}, initializes \tcode{val} with \tcode{rhs.val}, so that \tcode{*this} and \tcode{rhs} refer to the same object; otherwise, initializes \tcode{unex} with \tcode{std::move(rhs.unex)}. + +\pnum +\ensures +\tcode{rhs.has_value() == this->has_value()}. + +\pnum +\remarks +This constructor is trivial. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template constexpr expected(in_place_t, Args&&...); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\remarks +\tcode{expected} has no in-place value constructor: \tcode{T&} cannot be constructed in-place. Pass a \tcode{U} convertible to \tcode{T&} instead. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +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); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{remove_cvref_t} is not \tcode{in_place_t}, \tcode{expected}, or a specialization of \tcode{unexpected}; \tcode{is_constructible_v} is \tcode{true}; and \tcode{reference_constructs_from_temporary_v} is \tcode{false}. + +\pnum +\effects +Let \tcode{r} be the lvalue result of \tcode{T& r = std::forward(u);}. Initializes \tcode{val} with \tcode{addressof(r)}. + +\pnum +\ensures +\tcode{has_value()} is \tcode{true}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + requires(reference_constructs_from_temporary_v) +constexpr expected(U&&); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\remarks +A constructor for which \tcode{reference_constructs_from_temporary_v} is \tcode{true} — one that would bind \tcode{T&} to a temporary — is defined as deleted. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + 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); +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); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}; \tcode{reference_constructs_from_temporary_v} is \tcode{false}; and \tcode{is_constructible_v} is \tcode{true}. + +\pnum +\effects +If \tcode{rhs.has_value()} is \tcode{true}, initializes \tcode{val} with \tcode{addressof(*rhs)}, so that \tcode{*this} refers to the object referred to by \tcode{rhs}; otherwise, initializes \tcode{unex} with the error of \tcode{rhs}. No object referred to by \tcode{rhs} is moved from. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +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); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}; \tcode{reference_constructs_from_temporary_v} is \tcode{false}; \tcode{G} is a reference type; and \tcode{is_convertible_v} is \tcode{true}. + +\pnum +\effects +If \tcode{rhs.has_value()} is \tcode{true}, initializes \tcode{val} with \tcode{addressof(*rhs)}, so that \tcode{*this} refers to the object referred to by \tcode{rhs}; otherwise, initializes \tcode{unex} with the error of \tcode{rhs}. No object referred to by \tcode{rhs} is moved from. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +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); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\effects +Initializes \tcode{unex} with the error of \tcode{e}. + +\pnum +\ensures +\tcode{has_value()} is \tcode{false}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +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; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_reference_v} is \tcode{true}; \tcode{is_convertible_v} is \tcode{true}; and \tcode{reference_constructs_from_temporary_v} is \tcode{false}. + +\pnum +\effects +Initializes \tcode{unex} with the error of \tcode{e}. + +\pnum +\ensures +\tcode{has_value()} is \tcode{false}. + +\pnum +\remarks +This constructor never throws: the referent is bound, not copied. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + requires(is_reference_v && !is_reference_v) +constexpr expected(const unexpected&); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\remarks +When \tcode{E} is a reference type, an overload taking \tcode{unexpected} for a non-reference \tcode{G} is defined as deleted: the referent would live inside the (possibly temporary) source \tcode{unexpected} object, and binding \tcode{E&} to it would dangle. Use an \tcode{unexpected} holding an external object instead. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + requires(is_constructible_v && !@\exposidnc{unexpect-dangles-v}@) +constexpr explicit expected(unexpect_t, Args&&... args); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\effects +Direct-non-list-initializes \tcode{unex} with \tcode{in_place} and \tcode{std::forward(args)...}. + +\pnum +\ensures +\tcode{has_value()} is \tcode{false}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + requires(@\exposidnc{unexpect-dangles-v}@) +constexpr expected(unexpect_t, Args&&...); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\remarks +When \tcode{E} is a reference type, an overload with the same parameter types is defined as deleted if the single argument would bind \tcode{E&} to a temporary, or if it is otherwise not usable to construct \tcode{E}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + requires(!is_reference_v && is_constructible_v&, Args...>) +constexpr explicit expected(unexpect_t, initializer_list il, Args&&... args); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_reference_v} is \tcode{false}, and \tcode{is_constructible_v&, Args...>} is \tcode{true}. + +\pnum +\effects +Direct-non-list-initializes \tcode{unex} with \tcode{in_place}, \tcode{il}, and \tcode{std::forward(args)...}. + +\pnum +\ensures +\tcode{has_value()} is \tcode{false}. + +\pnum +\remarks +An overload with the same parameter types is defined as deleted when \tcode{E} is an lvalue reference type. An initializer list cannot provide the required long-lived error referent. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + requires is_reference_v +constexpr expected(unexpect_t, initializer_list, Args&&...); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\remarks +An overload with the same parameter types is defined as deleted when \tcode{E} is an lvalue reference type. An initializer list cannot provide the required long-lived error referent. +\end{itemdescr} + +\rSec3[expected.ref.dtor]{Destructor} + +\indexlibrarydtor{expected}% +\begin{itemdecl} +constexpr ~expected(); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_trivially_destructible_v} is \tcode{true}. + +\pnum +\effects +None: \tcode{*this} never owns the object it refers to; \tcode{T} is never destroyed. + +\pnum +\remarks +This destructor is trivial. +\end{itemdescr} + +\indexlibrarydtor{expected}% +\begin{itemdecl} +constexpr ~expected(); +\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}. \tcode{T} is not destroyed; \tcode{*this} never owns the object it refers to. + +\pnum +\remarks +This destructor is trivial if \tcode{E} is trivially destructible. +\end{itemdescr} + +\rSec3[expected.ref.assign]{Assignment} + +\indexlibrarymember{operator=}{expected}% +\begin{itemdecl} +constexpr expected& operator=(const expected&); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_trivially_copy_constructible_v} is \tcode{true}, \tcode{is_trivially_copy_assignable_v} is \tcode{true}, and \tcode{is_trivially_destructible_v} is \tcode{true}. + +\pnum +\effects +If \tcode{rhs.has_value()} is \tcode{true}: if \tcode{has_value()} is \tcode{true}, assigns \tcode{rhs.val} to \tcode{val}; otherwise destroys \tcode{unex} and initializes \tcode{val} with \tcode{rhs.val}. If \tcode{rhs.has_value()} is \tcode{false}, the error of \tcode{rhs} is assigned to or used to initialize \tcode{unex}, as for the primary template. In every case \tcode{*this} comes to refer to the object \tcode{rhs} refers to, or to hold the error of \tcode{rhs}. + +\pnum +\returns +\tcode{*this}. + +\pnum +\remarks +Assignment rebinds: assigning to an \tcode{expected} that holds a value changes which object it refers to. It never assigns through to the referent. This operator is trivial. +\end{itemdescr} + +\indexlibrarymember{operator=}{expected}% +\begin{itemdecl} +constexpr expected& operator=(const expected& rhs) noexcept( + is_nothrow_copy_constructible_v && is_nothrow_copy_assignable_v); +constexpr expected& operator=(expected&& rhs) noexcept( + is_nothrow_move_constructible_v && is_nothrow_move_assignable_v); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_reference_v || (is_copy_constructible_v && is_copy_assignable_v)} is \tcode{true} and \tcode{(is_trivially_copy_constructible_v && is_trivially_copy_assignable_v && + is_trivially_destructible_v)} is \tcode{false}. + +\pnum +\effects +If \tcode{rhs.has_value()} is \tcode{true}: if \tcode{has_value()} is \tcode{true}, assigns \tcode{rhs.val} to \tcode{val}; otherwise destroys \tcode{unex} and initializes \tcode{val} with \tcode{rhs.val}. If \tcode{rhs.has_value()} is \tcode{false}, the error of \tcode{rhs} is assigned to or used to initialize \tcode{unex}, as for the primary template. In every case \tcode{*this} comes to refer to the object \tcode{rhs} refers to, or to hold the error of \tcode{rhs}. + +\pnum +\returns +\tcode{*this}. + +\pnum +\remarks +This operator is defined as deleted unless \tcode{is_copy_assignable_v} is \tcode{true} and \tcode{is_copy_constructible_v} is \tcode{true}. +\end{itemdescr} + +\indexlibrarymember{operator=}{expected}% +\begin{itemdecl} +constexpr expected& operator=(expected&&) noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_trivially_move_constructible_v} is \tcode{true}, \tcode{is_trivially_move_assignable_v} is \tcode{true}, and \tcode{is_trivially_destructible_v} is \tcode{true}. + +\pnum +\effects +If \tcode{rhs.has_value()} is \tcode{true}: if \tcode{has_value()} is \tcode{true}, assigns \tcode{rhs.val} to \tcode{val}; otherwise destroys \tcode{unex} and initializes \tcode{val} with \tcode{rhs.val}. If \tcode{rhs.has_value()} is \tcode{false}, the error of \tcode{rhs} is assigned to or used to initialize \tcode{unex}, as for the primary template. + +\pnum +\returns +\tcode{*this}. + +\pnum +\remarks +This operator is trivial. +\end{itemdescr} + +\indexlibrarymember{operator=}{expected}% +\begin{itemdecl} +template + requires(!is_same_v, expected> && + !@\exposidnc{is-unexpected-specialization}@>::value && + is_constructible_v && !reference_constructs_from_temporary_v) +constexpr expected& operator=(U&& u); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{remove_cvref_t} is neither \tcode{expected} nor a specialization of \tcode{unexpected}, \tcode{is_constructible_v} is \tcode{true}, and \tcode{reference_constructs_from_temporary_v} is \tcode{false}. + +\pnum +\effects +Let \tcode{r} be the lvalue result of \tcode{T& r = std::forward(u);}. If \tcode{has_value()} is \tcode{true}, assigns \tcode{addressof(r)} to \tcode{val}. Otherwise, destroys \tcode{unex}, initializes \tcode{val} with \tcode{addressof(r)}, and sets \tcode{has_val} to \tcode{true}; if binding \tcode{r} throws, \tcode{*this} is left unchanged. + +\pnum +\returns +\tcode{*this}. +\end{itemdescr} + +\indexlibrarymember{operator=}{expected}% +\begin{itemdecl} +template + 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); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true} and \tcode{is_assignable_v} is \tcode{true}. + +\pnum +\effects +Makes \tcode{*this} hold the error of \tcode{e}, reinitializing \tcode{unex} from \tcode{e} rather than assigning through it. + +\pnum +\returns +\tcode{*this}. +\end{itemdescr} + +\indexlibrarymember{operator=}{expected}% +\begin{itemdecl} +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); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_reference_v} is \tcode{true}; \tcode{is_convertible_v} is \tcode{true}; and \tcode{reference_constructs_from_temporary_v} is \tcode{false}. + +\pnum +\effects +Makes \tcode{*this} hold the error of \tcode{e}, reinitializing \tcode{unex} from \tcode{e} rather than assigning through it. \tcode{unex.error()} thereafter refers to the same object as \tcode{e.error()}; the previously referenced object, if any, is not modified. + +\pnum +\returns +\tcode{*this}. + +\pnum +\remarks +This operator never throws: the referent is bound, not copied. +\end{itemdescr} + +\indexlibrarymember{operator=}{expected}% +\begin{itemdecl} +template + requires(is_reference_v && !is_reference_v) +constexpr expected& operator=(const unexpected&); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\remarks +When \tcode{E} is a reference type, an overload taking \tcode{unexpected} for a non-reference \tcode{G} is defined as deleted: it would rebind \tcode{unex} to \tcode{unexpected}'s temporary storage. +\end{itemdescr} + +\indexlibrarymember{emplace}{expected}% +\begin{itemdecl} +template + requires(is_constructible_v && !reference_constructs_from_temporary_v) +constexpr T& emplace(U&& u) noexcept(is_nothrow_constructible_v); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true} and \tcode{reference_constructs_from_temporary_v} is \tcode{false}. + +\pnum +\effects +Rebinds \tcode{*this} to refer to the object bound by \tcode{T& r = std::forward(u);}: if \tcode{has_value()} is \tcode{false}, destroys \tcode{unex} first. Sets \tcode{val} to \tcode{addressof(r)} and \tcode{has_val} to \tcode{true}. + +\pnum +\returns +\tcode{*val}. +\end{itemdescr} + +\rSec3[expected.ref.swap]{Swap} + +\indexlibrarymember{swap}{expected}% +\begin{itemdecl} +constexpr void swap(expected& rhs) noexcept(is_nothrow_move_constructible_v && + (is_reference_v || + is_nothrow_swappable_v)); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_reference_v || is_swappable_v} is \tcode{true} and \tcode{is_move_constructible_v} is \tcode{true}. + +\pnum +\effects +Exchanges the states of \tcode{*this} and \tcode{rhs}. When both hold values, exchanges \tcode{val} and \tcode{rhs.val} — the referenced objects are not swapped. Otherwise behaves as the primary template's \tcode{swap} does for the error. + +\pnum +\remarks +The exception specification is equivalent to \tcode{is_nothrow_move_constructible_v && (is_reference_v || is_nothrow_swappable_v)}. +\end{itemdescr} + +\indexlibraryglobal{swap}% +\begin{itemdecl} +friend constexpr void swap(expected& x, expected& y) noexcept(noexcept(x.swap(y))); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_reference_v || is_swappable_v} is \tcode{true} and \tcode{is_move_constructible_v} is \tcode{true}. + +\pnum +\effects +Equivalent to \tcode{x.swap(y)}. +\end{itemdescr} + +\rSec3[expected.ref.obs]{Observers} + +\indexlibrarymember{operator->}{expected}% +\begin{itemdecl} +constexpr T* operator->() const noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\expects +\tcode{has_value()} is \tcode{true}. + +\pnum +\returns +\tcode{val}. + +\pnum +\remarks +This is a \tcode{const} member function that returns a non-\tcode{const} \tcode{T*}; the constness of \tcode{*this} does not propagate to the referenced object. For deep \tcode{const}, use \tcode{expected}. +\end{itemdescr} + +\indexlibrarymember{operator*}{expected}% +\begin{itemdecl} +constexpr T& operator*() const noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\expects +\tcode{has_value()} is \tcode{true}. + +\pnum +\returns +\tcode{*val}. + +\pnum +\remarks +This is a \tcode{const} member function that returns a non-\tcode{const} \tcode{T&}; the constness of \tcode{*this} does not propagate to the referenced object. For deep \tcode{const}, use \tcode{expected}. +\end{itemdescr} + +\indexlibrarymember{operator bool}{expected}% +\indexlibrarymember{has_value}{expected}% +\begin{itemdecl} +constexpr explicit operator bool() const noexcept; +constexpr bool has_value() const noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\tcode{has_val}. +\end{itemdescr} + +\indexlibrarymember{value}{expected}% +\begin{itemdecl} +constexpr T& value() const&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{is_copy_constructible_v<\exposid{error-value-type}>} is \tcode{true}. + +\pnum +\returns +\tcode{*val} if \tcode{has_value()} is \tcode{true}. + +\pnum +\throws +\tcode{bad_expected_access(as_const(error()))} if \tcode{has_value()} is \tcode{false}. +\end{itemdescr} + +\indexlibrarymember{value}{expected}% +\begin{itemdecl} +constexpr T& value() &&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\tcode{*val} if \tcode{has_value()} is \tcode{true}. + +\pnum +\throws +\tcode{bad_expected_access(std::move(error()))} if \tcode{has_value()} is \tcode{false}. +\end{itemdescr} + +\indexlibrarymember{error}{expected}% +\begin{itemdecl} +constexpr const E& error() const& noexcept; +constexpr E& error() & noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\expects +\tcode{has_value()} is \tcode{false}. + +\pnum +\returns +\tcode{unex.error()}. +\end{itemdescr} + +\indexlibrarymember{error}{expected}% +\begin{itemdecl} +constexpr const E&& error() const&& noexcept; +constexpr E&& error() && noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\expects +\tcode{has_value()} is \tcode{false}. + +\pnum +\returns +\tcode{std::move(unex).error()}. +\end{itemdescr} + +\indexlibrarymember{value_or}{expected}% +\begin{itemdecl} +template> + requires(is_object_v && !is_array_v) +constexpr remove_cv_t value_or(U&& def) const; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{is_convertible_v>} and \tcode{is_convertible_v>} are \tcode{true}. + +\pnum +\returns +\tcode{has_value() ? static_cast>(*val) : static_cast>(std::forward(def))}. The result is an object, never a reference. +\end{itemdescr} + +\indexlibrarymember{error_or}{expected}% +\begin{itemdecl} +template + requires(is_copy_constructible_v>> && + is_convertible_v>>) +constexpr @\exposidnc{error-value-type}@ error_or(G&& def) const&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\tcode{std::forward(def)} if \tcode{has_value()} is \tcode{true}, \tcode{error()} otherwise. The result is an object, never a reference. +\end{itemdescr} + +\indexlibrarymember{error_or}{expected}% +\begin{itemdecl} +template + requires(is_move_constructible_v>> && + is_convertible_v>>) +constexpr @\exposidnc{error-value-type}@ error_or(G&& def) &&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\tcode{std::forward(def)} if \tcode{has_value()} is \tcode{true}, \tcode{std::move(error())} otherwise. The result is an object, never a reference. +\end{itemdescr} + +\rSec3[expected.ref.monadic]{Monadic operations} + +\indexlibrarymember{and_then}{expected}% +\begin{itemdecl} +template + requires is_constructible_v +constexpr auto and_then(F&& f) &; +template + requires is_constructible_v +constexpr auto and_then(F&& f) const&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\mandates +\tcode{remove_cvref_t>} is a specialization of \tcode{expected} and its \tcode{error_type} is the same type as \tcode{E}. + +\pnum +\effects +Equivalent to: \tcode{if (has_value()) return invoke(std::forward(f), *val); else return U(unexpect, error());} where \tcode{U} is \tcode{remove_cvref_t>}. + +\pnum +\remarks +The member templates \tcode{and_then}, \tcode{or_else}, \tcode{transform}, and \tcode{transform_error} behave as specified for the primary template, with one difference: the value is passed to the callable as \tcode{T&} for every ref-qualification of \tcode{*this}. An rvalue \tcode{expected} does not pass its referent as an rvalue; the object referred to is never moved from by these operations. +\end{itemdescr} + +\indexlibrarymember{and_then}{expected}% +\begin{itemdecl} +template + requires is_constructible_v +constexpr auto and_then(F&& f) &&; +template + requires is_constructible_v +constexpr auto and_then(F&& f) const&&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\mandates +\tcode{remove_cvref_t>} is a specialization of \tcode{expected} and its \tcode{error_type} is the same type as \tcode{E}. + +\pnum +\effects +Equivalent to: \tcode{if (has_value()) return invoke(std::forward(f), *val); else return U(unexpect, std::move(error()));} where \tcode{U} is \tcode{remove_cvref_t>}. +\end{itemdescr} + +\indexlibrarymember{or_else}{expected}% +\begin{itemdecl} +template constexpr auto or_else(F&& f) &; +template constexpr auto or_else(F&& f) const&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{remove_cvref_t>} is a specialization of \tcode{expected} and its \tcode{value_type} is the same type as \tcode{T&}. + +\pnum +\effects +Equivalent to: \tcode{if (has_value()) return G(*val); else return invoke(std::forward(f), error());} where \tcode{G} is \tcode{remove_cvref_t>}. +\end{itemdescr} + +\indexlibrarymember{or_else}{expected}% +\begin{itemdecl} +template constexpr auto or_else(F&& f) &&; +template constexpr auto or_else(F&& f) const&&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{remove_cvref_t>} is a specialization of \tcode{expected} and its \tcode{value_type} is the same type as \tcode{T&}. + +\pnum +\effects +Equivalent to: \tcode{if (has_value()) return G(*val); else return invoke(std::forward(f), std::move(error()));} where \tcode{G} is \tcode{remove_cvref_t>}. +\end{itemdescr} + +\indexlibrarymember{transform}{expected}% +\begin{itemdecl} +template + requires is_constructible_v +constexpr auto transform(F&& f) &; +template + requires is_constructible_v +constexpr auto transform(F&& f) const&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\effects +Equivalent to: \tcode{if (!has_value()) return U(unexpect, error()); else return expected(in_place, invoke(std::forward(f), *val));} where \tcode{U2} is \tcode{remove_cv_t>} and \tcode{U} is \tcode{expected}. +\end{itemdescr} + +\indexlibrarymember{transform}{expected}% +\begin{itemdecl} +template + requires is_constructible_v +constexpr auto transform(F&& f) &&; +template + requires is_constructible_v +constexpr auto transform(F&& f) const&&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\effects +Equivalent to: \tcode{if (!has_value()) return U(unexpect, std::move(error())); else return expected(in_place, invoke(std::forward(f), *val));} where \tcode{U2} is \tcode{remove_cv_t>} and \tcode{U} is \tcode{expected}. +\end{itemdescr} + +\indexlibrarymember{transform_error}{expected}% +\begin{itemdecl} +template constexpr auto transform_error(F&& f) &; +template constexpr auto transform_error(F&& f) const&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\begin{itemize} +\item \tcode{is_object_v} is \tcode{true}, +\item \tcode{is_array_v} is \tcode{false}, +\item \tcode{is_same_v>} is \tcode{true}, +\item \tcode{\exposid{is-unexpected-specialization}::value} is \tcode{false}. +\end{itemize} + +\pnum +\effects +Equivalent to: \tcode{if (has_value()) return G(*val); else return expected(unexpect, invoke(std::forward(f), error()));} where \tcode{G2} is \tcode{remove_cv_t>} and \tcode{G} is \tcode{expected}. +\end{itemdescr} + +\indexlibrarymember{transform_error}{expected}% +\begin{itemdecl} +template constexpr auto transform_error(F&& f) &&; +template constexpr auto transform_error(F&& f) const&&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\begin{itemize} +\item \tcode{is_object_v} is \tcode{true}, +\item \tcode{is_array_v} is \tcode{false}, +\item \tcode{is_same_v>} is \tcode{true}, +\item \tcode{\exposid{is-unexpected-specialization}::value} is \tcode{false}. +\end{itemize} + +\pnum +\effects +Equivalent to: \tcode{if (has_value()) return G(*val); else return expected(unexpect, invoke(std::forward(f), std::move(error())));} where \tcode{G2} is \tcode{remove_cv_t>} and \tcode{G} is \tcode{expected}. +\end{itemdescr} + +\rSec3[expected.ref.eq]{Equality operators} + +\indexlibraryglobal{operator==}% +\begin{itemdecl} +template + requires(!is_void_v) +friend constexpr bool operator==(const expected& x, const expected& y); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{!is_void_v} is \tcode{true}. The expression \tcode{*x == *y} is well-formed and its result is convertible to \tcode{bool}. The expression \tcode{x.error() == y.error()} is well-formed and its result is convertible to \tcode{bool}. + +\pnum +\returns +If \tcode{x.has_value() != y.has_value()}, \tcode{false}; otherwise, if \tcode{x.has_value()} is \tcode{true}, \tcode{*x == *y}; otherwise \tcode{x.error() == y.error()}. + +\pnum +\remarks +The equality operators behave as specified for the primary template, comparing referents through \tcode{operator*} and errors through \tcode{error()}. +\end{itemdescr} + +\indexlibraryglobal{operator==}% +\begin{itemdecl} +template + requires(!@\exposidnc{is-expected-specialization}@::value) +friend constexpr bool operator==(const expected& x, const T2& val); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{T2} is not a specialization of \tcode{expected}. The expression \tcode{*x == val} is well-formed and its result is convertible to \tcode{bool}. + +\pnum +\returns +\tcode{x.has_value() && static_cast(*x == val)}. +\end{itemdescr} + +\indexlibraryglobal{operator==}% +\begin{itemdecl} +template +friend constexpr bool operator==(const expected& x, const unexpected& e); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +The expression \tcode{x.error() == e.error()} is well-formed and its result is convertible to \tcode{bool}. + +\pnum +\returns +\tcode{!x.has_value() && static_cast(x.error() == e.error())}. +\end{itemdescr} diff --git a/papers/wording/fragments/expected.bad.tex b/papers/wording/fragments/expected.bad.tex new file mode 100644 index 0000000..5c47ae2 --- /dev/null +++ b/papers/wording/fragments/expected.bad.tex @@ -0,0 +1,100 @@ +\rSec2[expected.bad]{Class template bad_expected_access} + +\indexlibrarymember{what}{bad_expected_access}% +\begin{itemdecl} +const char* what() const noexcept override; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +An implementation-defined ntbs, which during constant evaluation is encoded with the ordinary literal encoding (\iref{lex.ccon}). +\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 +}; +\end{codeblock} + +\begin{itemdescr} +\pnum +\remarks +The class template \tcode{bad_expected_access} defines the type of objects thrown as exceptions to report the situation where an attempt is made to access the value of an \tcode{expected} object for which \tcode{has_value()} is \tcode{false}. +\end{itemdescr} + +\indexlibraryctor{bad_expected_access}% +\begin{itemdecl} +explicit bad_expected_access(E e); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Initializes \exposid{unex} with \tcode{std::move(e)}. +\end{itemdescr} + +\indexlibrarymember{what}{bad_expected_access}% +\begin{itemdecl} +const char* what() const noexcept override; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +An implementation-defined ntbs, which during constant evaluation is encoded with the ordinary literal encoding (\iref{lex.ccon}). +\end{itemdescr} + +\indexlibrarymember{error}{bad_expected_access}% +\begin{itemdecl} +constexpr E& error() & noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\exposid{unex}. +\end{itemdescr} + +\indexlibrarymember{error}{bad_expected_access}% +\begin{itemdecl} +constexpr const E& error() const& noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\exposid{unex}. +\end{itemdescr} + +\indexlibrarymember{error}{bad_expected_access}% +\begin{itemdecl} +constexpr E&& error() && noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\tcode{std::move(unex)}. +\end{itemdescr} + +\indexlibrarymember{error}{bad_expected_access}% +\begin{itemdecl} +constexpr const E&& error() const&& noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\tcode{std::move(unex)}. +\end{itemdescr} diff --git a/papers/wording/fragments/expected.bad.void.tex b/papers/wording/fragments/expected.bad.void.tex new file mode 100644 index 0000000..55b9c7d --- /dev/null +++ b/papers/wording/fragments/expected.bad.void.tex @@ -0,0 +1,21 @@ +\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; +}; +\end{codeblock} diff --git a/papers/wording/fragments/expected.expected.tex b/papers/wording/fragments/expected.expected.tex new file mode 100644 index 0000000..6fcff0a --- /dev/null +++ b/papers/wording/fragments/expected.expected.tex @@ -0,0 +1,1679 @@ +\rSec2[expected.expected]{Class template expected} + +\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 && + 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_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} +\pnum +\mandates +A program that instantiates the definition of \tcode{expected} with a \tcode{T} that is not a valid value type for \tcode{expected} (that is, \tcode{remove_cv_t} is \tcode{void}, or a complete non-array object type other than \tcode{in_place_t}, \tcode{unexpect_t}, or a specialization of \tcode{unexpected}) is ill-formed. A program that instantiates the definition of \tcode{expected} with an \tcode{E} that is not a valid template argument for \tcode{unexpected} is ill-formed. + +\pnum +\remarks +Any object of type \tcode{expected} either contains a value of type \tcode{T} or a value of type \tcode{E} nested within it. Member \tcode{has_val} indicates whether the \tcode{expected} object contains an object of type \tcode{T}. When \tcode{has_value()} is \tcode{false}, the error is \tcode{unex.error()}. The error is held as an \tcode{unexpected}, and not as an \tcode{E}, so that \tcode{E} may be an lvalue reference type: an \tcode{E&} cannot be a union member, whereas \tcode{unexpected} holds a pointer to an external object. +\end{itemdescr} + +\rSec3[expected.object.cons]{Constructors} + +\indexlibraryctor{expected}% +\begin{itemdecl} +constexpr expected() noexcept(is_nothrow_default_constructible_v); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_default_constructible_v} is \tcode{true}. + +\pnum +\effects +Value-initializes \tcode{val}. + +\pnum +\ensures +\tcode{has_value()} is \tcode{true}. + +\pnum +\throws +Any exception thrown by the initialization of \tcode{val}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +constexpr expected(const expected&) = default; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Direct-non-list-initializes \tcode{val} or \tcode{unex} (matching \tcode{rhs}'s active member) by trivial copy construction. + +\pnum +\ensures +\tcode{rhs.has_value() == this->has_value()}. + +\pnum +\remarks +This constructor is trivial. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +constexpr expected(const expected& rhs) noexcept(is_nothrow_copy_constructible_v && + is_nothrow_copy_constructible_v); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_copy_constructible_v} is \tcode{true}, \tcode{is_copy_constructible_v} is \tcode{true}, and \tcode{(is_trivially_copy_constructible_v && is_trivially_copy_constructible_v)} is \tcode{false}. + +\pnum +\effects +If \tcode{rhs.has_value()} is \tcode{true}, direct-non-list-initializes \tcode{val} with \tcode{*rhs}. Otherwise, direct-non-list-initializes \tcode{unex} with \tcode{rhs.error()}. + +\pnum +\ensures +\tcode{rhs.has_value() == this->has_value()}. + +\pnum +\throws +Any exception thrown by the initialization of \tcode{val} or \tcode{unex}. + +\pnum +\remarks +This constructor is defined as deleted unless \tcode{is_copy_constructible_v} is \tcode{true} and \tcode{is_copy_constructible_v} is \tcode{true} or \tcode{is_reference_v} is \tcode{true}. This constructor is trivial if \tcode{is_trivially_copy_constructible_v} is \tcode{true} and \tcode{is_trivially_copy_constructible_v} is \tcode{true}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +constexpr expected(expected&&) = default; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Direct-non-list-initializes \tcode{val} or \tcode{unex} (matching \tcode{rhs}'s active member) by trivial move construction. + +\pnum +\ensures +\tcode{rhs.has_value() == this->has_value()}. + +\pnum +\remarks +This constructor is trivial. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +constexpr expected(expected&& rhs) noexcept(is_nothrow_move_constructible_v && + is_nothrow_move_constructible_v); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_move_constructible_v} is \tcode{true}, \tcode{is_move_constructible_v} is \tcode{true}, and \tcode{(is_trivially_move_constructible_v && is_trivially_move_constructible_v)} is \tcode{false}. + +\pnum +\effects +If \tcode{rhs.has_value()} is \tcode{true}, direct-non-list-initializes \tcode{val} with \tcode{std::move(*rhs)}. Otherwise, direct-non-list-initializes \tcode{unex} with \tcode{std::move(rhs.error())}. + +\pnum +\ensures +\tcode{rhs.has_value()} is unchanged; \tcode{rhs.has_value() == this->has_value()} is \tcode{true}. + +\pnum +\throws +Any exception thrown by the initialization of \tcode{val} or \tcode{unex}. + +\pnum +\remarks +This constructor is trivial if \tcode{is_trivially_move_constructible_v} is \tcode{true} and \tcode{is_trivially_move_constructible_v} is \tcode{true}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +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); +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); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}; and \tcode{is_constructible_v} is \tcode{true}; and if \tcode{T} is not \tcode{bool}, \tcode{converts-from-any-cvref>} is \tcode{false}; and \tcode{is_constructible_v, expected&>} is \tcode{false}; and \tcode{is_constructible_v, expected>} is \tcode{false}; and \tcode{is_constructible_v, const expected&>} is \tcode{false}; and \tcode{is_constructible_v, const expected>} is \tcode{false}. + +\pnum +\effects +If \tcode{rhs.has_value()}, direct-non-list-initializes \tcode{val} with \tcode{*rhs}. Otherwise, direct-non-list-initializes \tcode{unex} with \tcode{rhs.error()}. + +\pnum +\ensures +\tcode{rhs.has_value()} is unchanged; \tcode{rhs.has_value() == this->has_value()} is \tcode{true}. + +\pnum +\throws +Any exception thrown by the initialization of \tcode{val} or \tcode{unex}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +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); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_reference_v} is \tcode{true} and \tcode{is_convertible_v} is \tcode{true}. + +\pnum +\effects +If \tcode{rhs.has_value()}, direct-non-list-initializes \tcode{val} with \tcode{*rhs}. Otherwise, direct-non-list-initializes \tcode{unex} with \tcode{rhs.error()}. + +\pnum +\ensures +\tcode{rhs.has_value()} is unchanged; \tcode{rhs.has_value() == this->has_value()} is \tcode{true}. + +\pnum +\throws +Any exception thrown by the initialization of \tcode{val} or \tcode{unex}. + +\pnum +\remarks +Unlike the value-\tcode{E} overload above, this overload participates in overload resolution only when \tcode{E} and \tcode{G} are both reference types, so the referenced error object is never copied. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +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); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_same_v, in_place_t>} is \tcode{false}; and \tcode{is_same_v, unexpect_t>} is \tcode{false}; and \tcode{is_same_v, expected>} is \tcode{false}; and \tcode{is_constructible_v} is \tcode{true}; and \tcode{remove_cvref_t} is not a specialization of \tcode{unexpected}; and if \tcode{T} is \tcode{bool}, \tcode{remove_cvref_t} is not a specialization of \tcode{expected}. + +\pnum +\effects +Direct-non-list-initializes \tcode{val} with \tcode{std::forward(v)}. + +\pnum +\ensures +\tcode{has_value()} is \tcode{true}. + +\pnum +\throws +Any exception thrown by the initialization of \tcode{val}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +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); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\effects +Direct-non-list-initializes \tcode{unex} with \tcode{std::forward(e.error())}. + +\pnum +\ensures +\tcode{has_value()} is \tcode{false}. + +\pnum +\throws +Any exception thrown by the initialization of \tcode{unex}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +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; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_reference_v} is \tcode{true}; and \tcode{is_constructible_v} is \tcode{true}; and \tcode{reference_constructs_from_temporary_v} is \tcode{false}. + +\pnum +\effects +Initializes \tcode{unex} with \tcode{e.error()}. + +\pnum +\ensures +\tcode{has_value()} is \tcode{false}. + +\pnum +\remarks +This constructor never throws: the referent is bound, not copied. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + requires(is_reference_v && !is_reference_v) +constexpr expected(const unexpected&); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\remarks +When \tcode{E} is a reference type, an overload taking \tcode{unexpected} for a non-reference \tcode{G} is defined as deleted: the referent would live inside the (possibly temporary) source \tcode{unexpected} object, and binding \tcode{E&} to it would dangle. Use an \tcode{unexpected} holding an external object instead. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + requires is_constructible_v +constexpr explicit expected(in_place_t, Args&&... args); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\effects +Direct-non-list-initializes \tcode{val} with \tcode{std::forward(args)...}. + +\pnum +\ensures +\tcode{has_value()} is \tcode{true}. + +\pnum +\throws +Any exception thrown by the initialization of \tcode{val}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + requires is_constructible_v&, Args...> +constexpr explicit expected(in_place_t, initializer_list il, Args&&... args); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v&, Args...>} is \tcode{true}. + +\pnum +\effects +Direct-non-list-initializes \tcode{val} with \tcode{il, std::forward(args)...}. + +\pnum +\ensures +\tcode{has_value()} is \tcode{true}. + +\pnum +\throws +Any exception thrown by the initialization of \tcode{val}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + requires(is_constructible_v && !@\exposidnc{unexpect-dangles-v}@) +constexpr explicit expected(unexpect_t, Args&&... args); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\effects +Direct-non-list-initializes \tcode{unex} with \tcode{std::forward(args)...}. + +\pnum +\ensures +\tcode{has_value()} is \tcode{false}. + +\pnum +\throws +Any exception thrown by the initialization of \tcode{unex}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + requires(@\exposidnc{unexpect-dangles-v}@) +constexpr expected(unexpect_t, Args&&...); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\remarks +When \tcode{E} is a reference type, an overload with the same parameter types is defined as deleted if the single argument would bind \tcode{E&} to a temporary, or if it is otherwise not usable to construct \tcode{E}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + requires(!is_reference_v && is_constructible_v&, Args...>) +constexpr explicit expected(unexpect_t, initializer_list il, Args&&... args); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_reference_v} is \tcode{false}, and \tcode{is_constructible_v&, Args...>} is \tcode{true}. + +\pnum +\effects +Direct-non-list-initializes \tcode{unex} with \tcode{il, std::forward(args)...}. + +\pnum +\ensures +\tcode{has_value()} is \tcode{false}. + +\pnum +\throws +Any exception thrown by the initialization of \tcode{unex}. + +\pnum +\remarks +An overload with the same parameter types is defined as deleted when \tcode{E} is an lvalue reference type. An initializer list cannot provide the required long-lived error referent. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + requires is_reference_v +constexpr expected(unexpect_t, initializer_list, Args&&...); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\remarks +An overload with the same parameter types is defined as deleted when \tcode{E} is an lvalue reference type. An initializer list cannot provide the required long-lived error referent. +\end{itemdescr} + +\rSec3[expected.object.dtor]{Destructor} + +\indexlibrarydtor{expected}% +\begin{itemdecl} +constexpr ~expected(); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_trivially_destructible_v} is \tcode{true} and \tcode{is_trivially_destructible_v} is \tcode{true}. + +\pnum +\effects +None: \tcode{val} or \tcode{unex} (whichever is active) has a trivial destructor. + +\pnum +\remarks +This destructor is trivial. +\end{itemdescr} + +\indexlibrarydtor{expected}% +\begin{itemdecl} +constexpr ~expected(); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{(is_trivially_destructible_v && is_trivially_destructible_v)} is \tcode{false}. + +\pnum +\effects +If \tcode{has_value()} is \tcode{true}, destroys \tcode{val}, otherwise destroys \tcode{unex}. +\end{itemdescr} + +\rSec3[expected.object.assign]{Assignment} + +\indexlibrarymember{operator=}{expected}% +\begin{itemdecl} +constexpr expected& operator=(const expected&); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\begin{itemize} +\item \tcode{is_trivially_copy_constructible_v} is \tcode{true}, +\item \tcode{is_trivially_copy_assignable_v} is \tcode{true}, +\item \tcode{is_trivially_destructible_v} is \tcode{true}, +\item \tcode{is_trivially_copy_constructible_v} is \tcode{true}, +\item \tcode{is_trivially_copy_assignable_v} is \tcode{true}, +\item \tcode{is_trivially_destructible_v} is \tcode{true}. +\end{itemize} + +\pnum +\effects +Trivially copies \tcode{rhs}'s active member into \tcode{*this}. + +\pnum +\returns +\tcode{*this}. + +\pnum +\remarks +This operator is trivial. +\end{itemdescr} + +\indexlibrarymember{operator=}{expected}% +\begin{itemdecl} +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); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\begin{itemize} +\item \tcode{is_copy_constructible_v} is \tcode{true}, +\item \tcode{is_copy_assignable_v} is \tcode{true}, +\item \tcode{is_reference_v || (is_copy_constructible_v && is_copy_assignable_v)} is \tcode{true}, +\item \tcode{is_nothrow_move_constructible_v || is_nothrow_move_constructible_v} is \tcode{true}, +\item \tcode{(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)} is \tcode{false}. +\end{itemize} + +\pnum +\effects +If \tcode{this->has_value() && rhs.has_value()}, equivalent to \tcode{val = *rhs}. Otherwise, if \tcode{this->has_value()}, equivalent to \tcode{reinit-expected(unex, val, rhs.error())}. Otherwise, if \tcode{rhs.has_value()}, equivalent to \tcode{reinit-expected(val, unex, *rhs)}. Otherwise, equivalent to \tcode{unex = rhs.unex}. Then, if no exception was thrown, equivalent to: \tcode{has_val = rhs.has_value(); return *this;} When \tcode{E} is an lvalue reference type, each of the cases above that initializes or assigns \tcode{unex} rebinds it: \tcode{unex} comes to refer to the same object as \tcode{rhs}'s error. No previously or subsequently referenced object is assigned through. + +\pnum +\returns +\tcode{*this}. + +\pnum +\remarks +This operator is defined as deleted unless \tcode{is_copy_assignable_v} is \tcode{true} and \tcode{is_copy_constructible_v} is \tcode{true} and \tcode{is_copy_assignable_v} is \tcode{true} or \tcode{is_reference_v} is \tcode{true} and \tcode{is_copy_constructible_v} is \tcode{true} or \tcode{is_reference_v} is \tcode{true} and \tcode{is_nothrow_move_constructible_v || is_nothrow_move_constructible_v} is \tcode{true}. This operator is trivial if \tcode{is_trivially_copy_constructible_v}, \tcode{is_trivially_copy_assignable_v}, \tcode{is_trivially_destructible_v}, \tcode{is_trivially_copy_constructible_v}, \tcode{is_trivially_copy_assignable_v}, and \tcode{is_trivially_destructible_v} are all \tcode{true}. +\end{itemdescr} + +\indexlibrarymember{operator=}{expected}% +\begin{itemdecl} +constexpr expected& operator=(expected&&) noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\begin{itemize} +\item \tcode{is_trivially_move_constructible_v} is \tcode{true}, +\item \tcode{is_trivially_move_assignable_v} is \tcode{true}, +\item \tcode{is_trivially_destructible_v} is \tcode{true}, +\item \tcode{is_trivially_move_constructible_v} is \tcode{true}, +\item \tcode{is_trivially_move_assignable_v} is \tcode{true}, +\item \tcode{is_trivially_destructible_v} is \tcode{true}. +\end{itemize} + +\pnum +\effects +Trivially moves \tcode{rhs}'s active member into \tcode{*this}. + +\pnum +\returns +\tcode{*this}. + +\pnum +\remarks +This operator is trivial. +\end{itemdescr} + +\indexlibrarymember{operator=}{expected}% +\begin{itemdecl} +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); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\begin{itemize} +\item \tcode{is_move_constructible_v} is \tcode{true}, +\item \tcode{is_move_assignable_v} is \tcode{true}, +\item \tcode{is_reference_v || (is_move_constructible_v && is_move_assignable_v)} is \tcode{true}, +\item \tcode{is_nothrow_move_constructible_v || is_nothrow_move_constructible_v} is \tcode{true}, +\item \tcode{(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)} is \tcode{false}. +\end{itemize} + +\pnum +\effects +If \tcode{this->has_value() && rhs.has_value()}, equivalent to \tcode{val = std::move(*rhs)}. Otherwise, if \tcode{this->has_value()}, equivalent to \tcode{reinit-expected(unex, val, std::move(rhs.error()))}. Otherwise, if \tcode{rhs.has_value()}, equivalent to \tcode{reinit-expected(val, unex, std::move(*rhs))}. Otherwise, equivalent to \tcode{unex = std::move(rhs.unex)}. Then, if no exception was thrown, equivalent to: \tcode{has_val = rhs.has_value(); return *this;} When \tcode{E} is an lvalue reference type, each of the cases above that initializes or assigns \tcode{unex} rebinds it: \tcode{unex} comes to refer to the same object as \tcode{rhs}'s error. No previously or subsequently referenced object is assigned through. + +\pnum +\returns +\tcode{*this}. + +\pnum +\remarks +The exception specification is equivalent to \tcode{is_nothrow_move_assignable_v && is_nothrow_move_constructible_v && is_nothrow_move_assignable_v && is_nothrow_move_constructible_v}. This operator is trivial if \tcode{is_trivially_move_constructible_v}, \tcode{is_trivially_move_assignable_v}, \tcode{is_trivially_destructible_v}, \tcode{is_trivially_move_constructible_v}, \tcode{is_trivially_move_assignable_v}, and \tcode{is_trivially_destructible_v} are all \tcode{true}. +\end{itemdescr} + +\indexlibrarymember{operator=}{expected}% +\begin{itemdecl} +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); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_same_v>} is \tcode{false}; and \tcode{remove_cvref_t} is not a specialization of \tcode{unexpected}; and \tcode{is_constructible_v} is \tcode{true}; and \tcode{is_assignable_v} is \tcode{true}; and \tcode{is_nothrow_constructible_v || is_nothrow_move_constructible_v || is_nothrow_move_constructible_v} is \tcode{true}. + +\pnum +\effects +If \tcode{has_value()} is \tcode{true}, equivalent to: \tcode{val = std::forward(v);} Otherwise, equivalent to: \tcode{reinit-expected(val, unex, std::forward(v)); has_val = true;} + +\pnum +\returns +\tcode{*this}. +\end{itemdescr} + +\indexlibrarymember{operator=}{expected}% +\begin{itemdecl} +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); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}; and \tcode{is_assignable_v} is \tcode{true}; and \tcode{is_nothrow_constructible_v || is_nothrow_move_constructible_v || is_nothrow_move_constructible_v} is \tcode{true}. + +\pnum +\effects +If \tcode{has_value()} is \tcode{true}, equivalent to: \tcode{reinit-expected(unex, val, e.error()); has_val = false;} Otherwise, equivalent to: \tcode{unex = unexpected(e.error());} + +\pnum +\returns +\tcode{*this}. +\end{itemdescr} + +\indexlibrarymember{operator=}{expected}% +\begin{itemdecl} +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); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_reference_v} is \tcode{true}; and \tcode{is_constructible_v} is \tcode{true}; and \tcode{reference_constructs_from_temporary_v} is \tcode{false}. + +\pnum +\effects +Rebinds \tcode{unex} to refer to the same object as \tcode{e.error()}, destroying \tcode{val} first if \tcode{has_value()} is \tcode{true}. + +\pnum +\ensures +\tcode{has_value()} is \tcode{false}. + +\pnum +\returns +\tcode{*this}. + +\pnum +\remarks +This operator never throws: the referent is bound, not copied. +\end{itemdescr} + +\indexlibrarymember{operator=}{expected}% +\begin{itemdecl} +template + requires(is_reference_v && !is_reference_v) +constexpr expected& operator=(const unexpected&); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\remarks +When \tcode{E} is a reference type, an overload taking \tcode{unexpected} for a non-reference \tcode{G} is defined as deleted: it would rebind \tcode{unex} to \tcode{unexpected}'s temporary storage. +\end{itemdescr} + +\indexlibrarymember{emplace}{expected}% +\begin{itemdecl} +template + requires is_nothrow_constructible_v +constexpr T& emplace(Args&&... args) noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_nothrow_constructible_v} is \tcode{true}. + +\pnum +\effects +Equivalent to: \tcode{if (has_value()) { destroy_at(addressof(val)); } else { destroy_at(addressof(unex)); has_val = true; } return *construct_at(addressof(val), std::forward(args)...);} +\end{itemdescr} + +\indexlibrarymember{emplace}{expected}% +\begin{itemdecl} +template + requires is_nothrow_constructible_v&, Args...> +constexpr T& emplace(initializer_list il, Args&&... args) noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_nothrow_constructible_v&, Args...>} is \tcode{true}. + +\pnum +\effects +Equivalent to: \tcode{if (has_value()) { destroy_at(addressof(val)); } else { destroy_at(addressof(unex)); has_val = true; } return *construct_at(addressof(val), il, std::forward(args)...);} +\end{itemdescr} + +\rSec3[expected.object.swap]{Swap} + +\indexlibrarymember{swap}{expected}% +\begin{itemdecl} +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)); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\begin{itemize} +\item \tcode{is_swappable_v} is \tcode{true}, +\item \tcode{is_reference_v || is_swappable_v} is \tcode{true}, +\item \tcode{is_move_constructible_v} is \tcode{true}, +\item \tcode{is_move_constructible_v} is \tcode{true}, +\item \tcode{is_nothrow_move_constructible_v || is_nothrow_move_constructible_v} is \tcode{true}. +\end{itemize} + +\pnum +\effects +If \tcode{this->has_value()} and \tcode{rhs.has_value()}, equivalent to \tcode{using std::swap; swap(val, rhs.val);}. If neither \tcode{*this} nor \tcode{rhs} contains a value, equivalent to \tcode{using std::swap; swap(unex, rhs.unex);}. If \tcode{rhs.has_value()} is \tcode{false} and \tcode{this->has_value()} is \tcode{true}, exchanges the value and error between \tcode{*this} and \tcode{rhs} (moving through a temporary so a failed move leaves both objects unchanged), leaving \tcode{has_value()} \tcode{false} and \tcode{rhs.has_value()} \tcode{true}. If \tcode{rhs.has_value()} is \tcode{true} and \tcode{this->has_value()} is \tcode{false}, equivalent to \tcode{rhs.swap(*this)}. + +\pnum +\throws +Any exception thrown by the expressions in the Effects. + +\pnum +\remarks +The exception specification is equivalent to \tcode{is_nothrow_move_constructible_v && is_nothrow_swappable_v && is_nothrow_move_constructible_v && (is_reference_v || is_nothrow_swappable_v)}. +\end{itemdescr} + +\indexlibraryglobal{swap}% +\begin{itemdecl} +friend constexpr void swap(expected& x, expected& y) noexcept(noexcept(x.swap(y))); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\begin{itemize} +\item \tcode{is_swappable_v} is \tcode{true}, +\item \tcode{is_reference_v || is_swappable_v} is \tcode{true}, +\item \tcode{is_move_constructible_v} is \tcode{true}, +\item \tcode{is_move_constructible_v} is \tcode{true}, +\item \tcode{is_nothrow_move_constructible_v || is_nothrow_move_constructible_v} is \tcode{true}. +\end{itemize} + +\pnum +\effects +Equivalent to \tcode{x.swap(y)}. +\end{itemdescr} + +\rSec3[expected.object.obs]{Observers} + +\indexlibrarymember{operator->}{expected}% +\begin{itemdecl} +constexpr const T* operator->() const noexcept; +constexpr T* operator->() noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\hardexpects +\tcode{has_value()} is \tcode{true}. + +\pnum +\returns +\tcode{addressof(val)}. +\end{itemdescr} + +\indexlibrarymember{operator*}{expected}% +\begin{itemdecl} +constexpr const T& operator*() const& noexcept; +constexpr T& operator*() & noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\hardexpects +\tcode{has_value()} is \tcode{true}. + +\pnum +\returns +\tcode{val}. +\end{itemdescr} + +\indexlibrarymember{operator*}{expected}% +\begin{itemdecl} +constexpr const T&& operator*() const&& noexcept; +constexpr T&& operator*() && noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\hardexpects +\tcode{has_value()} is \tcode{true}. + +\pnum +\returns +\tcode{std::move(val)}. +\end{itemdescr} + +\indexlibrarymember{operator bool}{expected}% +\indexlibrarymember{has_value}{expected}% +\begin{itemdecl} +constexpr explicit operator bool() const noexcept; +constexpr bool has_value() const noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\tcode{has_val}. +\end{itemdescr} + +\indexlibrarymember{value}{expected}% +\begin{itemdecl} +constexpr const T& value() const&; +constexpr T& value() &; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{is_copy_constructible_v} is \tcode{true}. + +\pnum +\returns +\tcode{val}, if \tcode{has_value()} is \tcode{true}. + +\pnum +\throws +\tcode{bad_expected_access(as_const(error()))} if \tcode{has_value()} is \tcode{false}. +\end{itemdescr} + +\indexlibrarymember{value}{expected}% +\begin{itemdecl} +constexpr const T&& value() const&&; +constexpr T&& value() &&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{is_copy_constructible_v} is \tcode{true} and \tcode{is_constructible_v} is \tcode{true}. + +\pnum +\returns +\tcode{std::move(val)}, if \tcode{has_value()} is \tcode{true}. + +\pnum +\throws +\tcode{bad_expected_access(std::move(error()))} if \tcode{has_value()} is \tcode{false}. +\end{itemdescr} + +\indexlibrarymember{error}{expected}% +\begin{itemdecl} +constexpr const E& error() const& noexcept; +constexpr E& error() & noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\hardexpects +\tcode{has_value()} is \tcode{false}. + +\pnum +\returns +\tcode{unex.error()}. +\end{itemdescr} + +\indexlibrarymember{error}{expected}% +\begin{itemdecl} +constexpr const E&& error() const&& noexcept; +constexpr E&& error() && noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\hardexpects +\tcode{has_value()} is \tcode{false}. + +\pnum +\returns +\tcode{std::move(unex).error()}. +\end{itemdescr} + +\indexlibrarymember{value_or}{expected}% +\begin{itemdecl} +template> constexpr T value_or(U&& def) const&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{is_copy_constructible_v} is \tcode{true} and \tcode{is_convertible_v} is \tcode{true}. + +\pnum +\returns +\tcode{has_value() ? **this : static_cast(std::forward(def))}. +\end{itemdescr} + +\indexlibrarymember{value_or}{expected}% +\begin{itemdecl} +template> constexpr T value_or(U&& def) &&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{is_move_constructible_v} is \tcode{true} and \tcode{is_convertible_v} is \tcode{true}. + +\pnum +\returns +\tcode{has_value() ? std::move(**this) : static_cast(std::forward(def))}. +\end{itemdescr} + +\indexlibrarymember{error_or}{expected}% +\begin{itemdecl} +template + requires(is_copy_constructible_v<@\exposidnc{error-value-type}@> && + is_convertible_v) +constexpr @\exposidnc{error-value-type}@ error_or(G&& def) const&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{is_copy_constructible_v} is \tcode{true} and \tcode{is_convertible_v} is \tcode{true}. + +\pnum +\returns +\tcode{std::forward(def)} if \tcode{has_value()} is \tcode{true}, \tcode{error()} otherwise. +\end{itemdescr} + +\indexlibrarymember{error_or}{expected}% +\begin{itemdecl} +template + requires(is_move_constructible_v<@\exposidnc{error-value-type}@> && + is_convertible_v) +constexpr @\exposidnc{error-value-type}@ error_or(G&& def) &&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{is_move_constructible_v} is \tcode{true} and \tcode{is_convertible_v} is \tcode{true}. + +\pnum +\returns +\tcode{std::forward(def)} if \tcode{has_value()} is \tcode{true}, \tcode{std::move(error())} otherwise. +\end{itemdescr} + +\rSec3[expected.object.monadic]{Monadic operations} + +\indexlibrarymember{and_then}{expected}% +\begin{itemdecl} +template + requires is_constructible_v +constexpr auto and_then(F&& f) &; +template + requires is_constructible_v +constexpr auto and_then(F&& f) const&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\mandates +\tcode{remove_cvref_t>} is a specialization of \tcode{expected} and its \tcode{error_type} is the same type as \tcode{E}. + +\pnum +\effects +Equivalent to: \tcode{if (has_value()) return invoke(std::forward(f), val); else return U(unexpect, error());} where \tcode{U} is \tcode{remove_cvref_t>}. +\end{itemdescr} + +\indexlibrarymember{and_then}{expected}% +\begin{itemdecl} +template + requires is_constructible_v +constexpr auto and_then(F&& f) &&; +template + requires is_constructible_v +constexpr auto and_then(F&& f) const&&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\mandates +\tcode{remove_cvref_t>} is a specialization of \tcode{expected} and its \tcode{error_type} is the same type as \tcode{E}. + +\pnum +\effects +Equivalent to: \tcode{if (has_value()) return invoke(std::forward(f), std::move(val)); else return U(unexpect, std::move(error()));} where \tcode{U} is \tcode{remove_cvref_t>}. +\end{itemdescr} + +\indexlibrarymember{or_else}{expected}% +\begin{itemdecl} +template + requires is_constructible_v +constexpr auto or_else(F&& f) &; +template + requires is_constructible_v +constexpr auto or_else(F&& f) const&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\mandates +\tcode{remove_cvref_t>} is a specialization of \tcode{expected} and its \tcode{value_type} is the same type as \tcode{T}. + +\pnum +\effects +Equivalent to: \tcode{if (has_value()) return G(in_place, val); else return invoke(std::forward(f), error());} where \tcode{G} is \tcode{remove_cvref_t>}. +\end{itemdescr} + +\indexlibrarymember{or_else}{expected}% +\begin{itemdecl} +template + requires is_constructible_v +constexpr auto or_else(F&& f) &&; +template + requires is_constructible_v +constexpr auto or_else(F&& f) const&&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\mandates +\tcode{remove_cvref_t>} is a specialization of \tcode{expected} and its \tcode{value_type} is the same type as \tcode{T}. + +\pnum +\effects +Equivalent to: \tcode{if (has_value()) return G(in_place, std::move(val)); else return invoke(std::forward(f), std::move(error()));} where \tcode{G} is \tcode{remove_cvref_t>}. +\end{itemdescr} + +\indexlibrarymember{transform}{expected}% +\begin{itemdecl} +template + requires is_constructible_v +constexpr auto transform(F&& f) &; +template + requires is_constructible_v +constexpr auto transform(F&& f) const&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\effects +Equivalent to: \tcode{if (!has_value()) return U(unexpect, error()); else return expected(in_place, invoke(std::forward(f), val));} where \tcode{U2} is \tcode{remove_cv_t>} and \tcode{U} is \tcode{expected}. +\end{itemdescr} + +\indexlibrarymember{transform}{expected}% +\begin{itemdecl} +template + requires is_constructible_v +constexpr auto transform(F&& f) &&; +template + requires is_constructible_v +constexpr auto transform(F&& f) const&&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\effects +Equivalent to: \tcode{if (!has_value()) return U(unexpect, std::move(error())); else return expected(in_place, invoke(std::forward(f), std::move(val)));} where \tcode{U2} is \tcode{remove_cv_t>} and \tcode{U} is \tcode{expected}. +\end{itemdescr} + +\indexlibrarymember{transform_error}{expected}% +\begin{itemdecl} +template + requires is_constructible_v +constexpr auto transform_error(F&& f) &; +template + requires is_constructible_v +constexpr auto transform_error(F&& f) const&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\mandates +\begin{itemize} +\item \tcode{is_object_v} is \tcode{true}, +\item \tcode{is_array_v} is \tcode{false}, +\item \tcode{is_same_v>} is \tcode{true}, +\item \tcode{\exposid{is-unexpected-specialization}::value} is \tcode{false}. +\end{itemize} + +\pnum +\effects +Equivalent to: \tcode{if (has_value()) return G(in_place, val); else return expected(unexpect, invoke(std::forward(f), error()));} where \tcode{G2} is \tcode{remove_cv_t>} and \tcode{G} is \tcode{expected}. +\end{itemdescr} + +\indexlibrarymember{transform_error}{expected}% +\begin{itemdecl} +template + requires is_constructible_v +constexpr auto transform_error(F&& f) &&; +template + requires is_constructible_v +constexpr auto transform_error(F&& f) const&&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\mandates +\begin{itemize} +\item \tcode{is_object_v} is \tcode{true}, +\item \tcode{is_array_v} is \tcode{false}, +\item \tcode{is_same_v>} is \tcode{true}, +\item \tcode{\exposid{is-unexpected-specialization}::value} is \tcode{false}. +\end{itemize} + +\pnum +\effects +Equivalent to: \tcode{if (has_value()) return G(in_place, std::move(val)); else return expected(unexpect, invoke(std::forward(f), std::move(error())));} where \tcode{G2} is \tcode{remove_cv_t>} and \tcode{G} is \tcode{expected}. +\end{itemdescr} + +\rSec3[expected.object.eq]{Equality operators} + +\indexlibraryglobal{operator==}% +\begin{itemdecl} +template + requires(!is_void_v) +friend constexpr bool operator==(const expected& x, const expected& y); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{!is_void_v} is \tcode{true}. The expression \tcode{*x == *y} is well-formed and its result is convertible to \tcode{bool}. The expression \tcode{x.error() == y.error()} is well-formed and its result is convertible to \tcode{bool}. + +\pnum +\returns +If \tcode{x.has_value() != y.has_value()}, \tcode{false}; otherwise, if \tcode{x.has_value()} is \tcode{true}, \tcode{*x == *y}; otherwise \tcode{x.error() == y.error()}. +\end{itemdescr} + +\indexlibraryglobal{operator==}% +\begin{itemdecl} +template + requires(!@\exposidnc{is-expected-specialization}@::value) +friend constexpr bool operator==(const expected& x, const T2& val); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{T2} is not a specialization of \tcode{expected}. The expression \tcode{*x == val} is well-formed and its result is convertible to \tcode{bool}. + +\pnum +\returns +\tcode{x.has_value() && static_cast(*x == val)}. +\end{itemdescr} + +\indexlibraryglobal{operator==}% +\begin{itemdecl} +template +friend constexpr bool operator==(const expected& x, const unexpected& e); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +The expression \tcode{x.error() == e.error()} is well-formed and its result is convertible to \tcode{bool}. + +\pnum +\returns +\tcode{!x.has_value() && static_cast(x.error() == e.error())}. +\end{itemdescr} diff --git a/papers/wording/fragments/expected.ref.tex b/papers/wording/fragments/expected.ref.tex new file mode 100644 index 0000000..fb4cda2 --- /dev/null +++ b/papers/wording/fragments/expected.ref.tex @@ -0,0 +1,1338 @@ +\rSec2[expected.ref]{Partial specialization of expected for reference types} + +\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 + }; +}; +\end{codeblock} + +\begin{itemdescr} +\pnum +\mandates +A program that instantiates the definition of \tcode{expected} with an \tcode{E} that is not a valid template argument for \tcode{unexpected} is ill-formed. \tcode{T} shall be an object type that is not an array type. + +\pnum +\remarks +An object of type \tcode{expected} either represents a reference to an object of type \tcode{T}, or holds an error. Member \tcode{has_val} indicates whether the object represents a reference. When it represents a reference, member \tcode{val} points to the referenced object, which is not owned by the \tcode{expected} object. Otherwise, the error is \tcode{unex.error()}. +\end{itemdescr} + +\rSec3[expected.ref.cons]{Constructors} + +\indexlibraryctor{expected}% +\begin{itemdecl} +; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\remarks +\tcode{expected} has no default constructor: a reference cannot be null, so there is no empty state to default-construct into. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +constexpr expected(const expected&) = default; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +If \tcode{rhs.has_value()} is \tcode{true}, initializes \tcode{val} with \tcode{rhs.val}, so that \tcode{*this} and \tcode{rhs} refer to the same object; otherwise, initializes \tcode{unex} with \tcode{rhs.unex}. + +\pnum +\ensures +\tcode{rhs.has_value() == this->has_value()}. + +\pnum +\remarks +This constructor is trivial. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +constexpr expected(const expected& rhs) noexcept(is_nothrow_copy_constructible_v); +constexpr expected(expected&& rhs) noexcept(is_nothrow_move_constructible_v); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_copy_constructible_v} is \tcode{true} and \tcode{is_trivially_copy_constructible_v} is \tcode{false}. + +\pnum +\effects +If \tcode{rhs.has_value()} is \tcode{true}, initializes \tcode{val} with \tcode{rhs.val}, so that \tcode{*this} and \tcode{rhs} refer to the same object; otherwise, initializes \tcode{unex} with \tcode{rhs.unex}. + +\pnum +\ensures +\tcode{rhs.has_value() == this->has_value()}. + +\pnum +\remarks +This constructor is trivial if the corresponding constructor of \tcode{E} is trivial, and is defined as deleted unless \tcode{is_copy_constructible_v} is \tcode{true}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +constexpr expected(expected&&) = default; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +If \tcode{rhs.has_value()} is \tcode{true}, initializes \tcode{val} with \tcode{rhs.val}, so that \tcode{*this} and \tcode{rhs} refer to the same object; otherwise, initializes \tcode{unex} with \tcode{std::move(rhs.unex)}. + +\pnum +\ensures +\tcode{rhs.has_value() == this->has_value()}. + +\pnum +\remarks +This constructor is trivial. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template constexpr expected(in_place_t, Args&&...); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\remarks +\tcode{expected} has no in-place value constructor: \tcode{T&} cannot be constructed in-place. Pass a \tcode{U} convertible to \tcode{T&} instead. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +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); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{remove_cvref_t} is not \tcode{in_place_t}, \tcode{expected}, or a specialization of \tcode{unexpected}; \tcode{is_constructible_v} is \tcode{true}; and \tcode{reference_constructs_from_temporary_v} is \tcode{false}. + +\pnum +\effects +Let \tcode{r} be the lvalue result of \tcode{T& r = std::forward(u);}. Initializes \tcode{val} with \tcode{addressof(r)}. + +\pnum +\ensures +\tcode{has_value()} is \tcode{true}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + requires(reference_constructs_from_temporary_v) +constexpr expected(U&&); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\remarks +A constructor for which \tcode{reference_constructs_from_temporary_v} is \tcode{true} — one that would bind \tcode{T&} to a temporary — is defined as deleted. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + 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); +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); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}; \tcode{reference_constructs_from_temporary_v} is \tcode{false}; and \tcode{is_constructible_v} is \tcode{true}. + +\pnum +\effects +If \tcode{rhs.has_value()} is \tcode{true}, initializes \tcode{val} with \tcode{addressof(*rhs)}, so that \tcode{*this} refers to the object referred to by \tcode{rhs}; otherwise, initializes \tcode{unex} with the error of \tcode{rhs}. No object referred to by \tcode{rhs} is moved from. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +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); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}; \tcode{reference_constructs_from_temporary_v} is \tcode{false}; \tcode{G} is a reference type; and \tcode{is_convertible_v} is \tcode{true}. + +\pnum +\effects +If \tcode{rhs.has_value()} is \tcode{true}, initializes \tcode{val} with \tcode{addressof(*rhs)}, so that \tcode{*this} refers to the object referred to by \tcode{rhs}; otherwise, initializes \tcode{unex} with the error of \tcode{rhs}. No object referred to by \tcode{rhs} is moved from. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +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); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\effects +Initializes \tcode{unex} with the error of \tcode{e}. + +\pnum +\ensures +\tcode{has_value()} is \tcode{false}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +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; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_reference_v} is \tcode{true}; \tcode{is_convertible_v} is \tcode{true}; and \tcode{reference_constructs_from_temporary_v} is \tcode{false}. + +\pnum +\effects +Initializes \tcode{unex} with the error of \tcode{e}. + +\pnum +\ensures +\tcode{has_value()} is \tcode{false}. + +\pnum +\remarks +This constructor never throws: the referent is bound, not copied. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + requires(is_reference_v && !is_reference_v) +constexpr expected(const unexpected&); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\remarks +When \tcode{E} is a reference type, an overload taking \tcode{unexpected} for a non-reference \tcode{G} is defined as deleted: the referent would live inside the (possibly temporary) source \tcode{unexpected} object, and binding \tcode{E&} to it would dangle. Use an \tcode{unexpected} holding an external object instead. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + requires(is_constructible_v && !@\exposidnc{unexpect-dangles-v}@) +constexpr explicit expected(unexpect_t, Args&&... args); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\effects +Direct-non-list-initializes \tcode{unex} with \tcode{in_place} and \tcode{std::forward(args)...}. + +\pnum +\ensures +\tcode{has_value()} is \tcode{false}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + requires(@\exposidnc{unexpect-dangles-v}@) +constexpr expected(unexpect_t, Args&&...); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\remarks +When \tcode{E} is a reference type, an overload with the same parameter types is defined as deleted if the single argument would bind \tcode{E&} to a temporary, or if it is otherwise not usable to construct \tcode{E}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + requires(!is_reference_v && is_constructible_v&, Args...>) +constexpr explicit expected(unexpect_t, initializer_list il, Args&&... args); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_reference_v} is \tcode{false}, and \tcode{is_constructible_v&, Args...>} is \tcode{true}. + +\pnum +\effects +Direct-non-list-initializes \tcode{unex} with \tcode{in_place}, \tcode{il}, and \tcode{std::forward(args)...}. + +\pnum +\ensures +\tcode{has_value()} is \tcode{false}. + +\pnum +\remarks +An overload with the same parameter types is defined as deleted when \tcode{E} is an lvalue reference type. An initializer list cannot provide the required long-lived error referent. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + requires is_reference_v +constexpr expected(unexpect_t, initializer_list, Args&&...); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\remarks +An overload with the same parameter types is defined as deleted when \tcode{E} is an lvalue reference type. An initializer list cannot provide the required long-lived error referent. +\end{itemdescr} + +\rSec3[expected.ref.dtor]{Destructor} + +\indexlibrarydtor{expected}% +\begin{itemdecl} +constexpr ~expected(); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_trivially_destructible_v} is \tcode{true}. + +\pnum +\effects +None: \tcode{*this} never owns the object it refers to; \tcode{T} is never destroyed. + +\pnum +\remarks +This destructor is trivial. +\end{itemdescr} + +\indexlibrarydtor{expected}% +\begin{itemdecl} +constexpr ~expected(); +\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}. \tcode{T} is not destroyed; \tcode{*this} never owns the object it refers to. + +\pnum +\remarks +This destructor is trivial if \tcode{E} is trivially destructible. +\end{itemdescr} + +\rSec3[expected.ref.assign]{Assignment} + +\indexlibrarymember{operator=}{expected}% +\begin{itemdecl} +constexpr expected& operator=(const expected&); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_trivially_copy_constructible_v} is \tcode{true}, \tcode{is_trivially_copy_assignable_v} is \tcode{true}, and \tcode{is_trivially_destructible_v} is \tcode{true}. + +\pnum +\effects +If \tcode{rhs.has_value()} is \tcode{true}: if \tcode{has_value()} is \tcode{true}, assigns \tcode{rhs.val} to \tcode{val}; otherwise destroys \tcode{unex} and initializes \tcode{val} with \tcode{rhs.val}. If \tcode{rhs.has_value()} is \tcode{false}, the error of \tcode{rhs} is assigned to or used to initialize \tcode{unex}, as for the primary template. In every case \tcode{*this} comes to refer to the object \tcode{rhs} refers to, or to hold the error of \tcode{rhs}. + +\pnum +\returns +\tcode{*this}. + +\pnum +\remarks +Assignment rebinds: assigning to an \tcode{expected} that holds a value changes which object it refers to. It never assigns through to the referent. This operator is trivial. +\end{itemdescr} + +\indexlibrarymember{operator=}{expected}% +\begin{itemdecl} +constexpr expected& operator=(const expected& rhs) noexcept( + is_nothrow_copy_constructible_v && is_nothrow_copy_assignable_v); +constexpr expected& operator=(expected&& rhs) noexcept( + is_nothrow_move_constructible_v && is_nothrow_move_assignable_v); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_reference_v || (is_copy_constructible_v && is_copy_assignable_v)} is \tcode{true} and \tcode{(is_trivially_copy_constructible_v && is_trivially_copy_assignable_v && + is_trivially_destructible_v)} is \tcode{false}. + +\pnum +\effects +If \tcode{rhs.has_value()} is \tcode{true}: if \tcode{has_value()} is \tcode{true}, assigns \tcode{rhs.val} to \tcode{val}; otherwise destroys \tcode{unex} and initializes \tcode{val} with \tcode{rhs.val}. If \tcode{rhs.has_value()} is \tcode{false}, the error of \tcode{rhs} is assigned to or used to initialize \tcode{unex}, as for the primary template. In every case \tcode{*this} comes to refer to the object \tcode{rhs} refers to, or to hold the error of \tcode{rhs}. + +\pnum +\returns +\tcode{*this}. + +\pnum +\remarks +This operator is defined as deleted unless \tcode{is_copy_assignable_v} is \tcode{true} and \tcode{is_copy_constructible_v} is \tcode{true}. +\end{itemdescr} + +\indexlibrarymember{operator=}{expected}% +\begin{itemdecl} +constexpr expected& operator=(expected&&) noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_trivially_move_constructible_v} is \tcode{true}, \tcode{is_trivially_move_assignable_v} is \tcode{true}, and \tcode{is_trivially_destructible_v} is \tcode{true}. + +\pnum +\effects +If \tcode{rhs.has_value()} is \tcode{true}: if \tcode{has_value()} is \tcode{true}, assigns \tcode{rhs.val} to \tcode{val}; otherwise destroys \tcode{unex} and initializes \tcode{val} with \tcode{rhs.val}. If \tcode{rhs.has_value()} is \tcode{false}, the error of \tcode{rhs} is assigned to or used to initialize \tcode{unex}, as for the primary template. + +\pnum +\returns +\tcode{*this}. + +\pnum +\remarks +This operator is trivial. +\end{itemdescr} + +\indexlibrarymember{operator=}{expected}% +\begin{itemdecl} +template + requires(!is_same_v, expected> && + !@\exposidnc{is-unexpected-specialization}@>::value && + is_constructible_v && !reference_constructs_from_temporary_v) +constexpr expected& operator=(U&& u); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{remove_cvref_t} is neither \tcode{expected} nor a specialization of \tcode{unexpected}, \tcode{is_constructible_v} is \tcode{true}, and \tcode{reference_constructs_from_temporary_v} is \tcode{false}. + +\pnum +\effects +Let \tcode{r} be the lvalue result of \tcode{T& r = std::forward(u);}. If \tcode{has_value()} is \tcode{true}, assigns \tcode{addressof(r)} to \tcode{val}. Otherwise, destroys \tcode{unex}, initializes \tcode{val} with \tcode{addressof(r)}, and sets \tcode{has_val} to \tcode{true}; if binding \tcode{r} throws, \tcode{*this} is left unchanged. + +\pnum +\returns +\tcode{*this}. +\end{itemdescr} + +\indexlibrarymember{operator=}{expected}% +\begin{itemdecl} +template + 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); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true} and \tcode{is_assignable_v} is \tcode{true}. + +\pnum +\effects +Makes \tcode{*this} hold the error of \tcode{e}, reinitializing \tcode{unex} from \tcode{e} rather than assigning through it. + +\pnum +\returns +\tcode{*this}. +\end{itemdescr} + +\indexlibrarymember{operator=}{expected}% +\begin{itemdecl} +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); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_reference_v} is \tcode{true}; \tcode{is_convertible_v} is \tcode{true}; and \tcode{reference_constructs_from_temporary_v} is \tcode{false}. + +\pnum +\effects +Makes \tcode{*this} hold the error of \tcode{e}, reinitializing \tcode{unex} from \tcode{e} rather than assigning through it. \tcode{unex.error()} thereafter refers to the same object as \tcode{e.error()}; the previously referenced object, if any, is not modified. + +\pnum +\returns +\tcode{*this}. + +\pnum +\remarks +This operator never throws: the referent is bound, not copied. +\end{itemdescr} + +\indexlibrarymember{operator=}{expected}% +\begin{itemdecl} +template + requires(is_reference_v && !is_reference_v) +constexpr expected& operator=(const unexpected&); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\remarks +When \tcode{E} is a reference type, an overload taking \tcode{unexpected} for a non-reference \tcode{G} is defined as deleted: it would rebind \tcode{unex} to \tcode{unexpected}'s temporary storage. +\end{itemdescr} + +\indexlibrarymember{emplace}{expected}% +\begin{itemdecl} +template + requires(is_constructible_v && !reference_constructs_from_temporary_v) +constexpr T& emplace(U&& u) noexcept(is_nothrow_constructible_v); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true} and \tcode{reference_constructs_from_temporary_v} is \tcode{false}. + +\pnum +\effects +Rebinds \tcode{*this} to refer to the object bound by \tcode{T& r = std::forward(u);}: if \tcode{has_value()} is \tcode{false}, destroys \tcode{unex} first. Sets \tcode{val} to \tcode{addressof(r)} and \tcode{has_val} to \tcode{true}. + +\pnum +\returns +\tcode{*val}. +\end{itemdescr} + +\rSec3[expected.ref.swap]{Swap} + +\indexlibrarymember{swap}{expected}% +\begin{itemdecl} +constexpr void swap(expected& rhs) noexcept(is_nothrow_move_constructible_v && + (is_reference_v || + is_nothrow_swappable_v)); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_reference_v || is_swappable_v} is \tcode{true} and \tcode{is_move_constructible_v} is \tcode{true}. + +\pnum +\effects +Exchanges the states of \tcode{*this} and \tcode{rhs}. When both hold values, exchanges \tcode{val} and \tcode{rhs.val} — the referenced objects are not swapped. Otherwise behaves as the primary template's \tcode{swap} does for the error. + +\pnum +\remarks +The exception specification is equivalent to \tcode{is_nothrow_move_constructible_v && (is_reference_v || is_nothrow_swappable_v)}. +\end{itemdescr} + +\indexlibraryglobal{swap}% +\begin{itemdecl} +friend constexpr void swap(expected& x, expected& y) noexcept(noexcept(x.swap(y))); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_reference_v || is_swappable_v} is \tcode{true} and \tcode{is_move_constructible_v} is \tcode{true}. + +\pnum +\effects +Equivalent to \tcode{x.swap(y)}. +\end{itemdescr} + +\rSec3[expected.ref.obs]{Observers} + +\indexlibrarymember{operator->}{expected}% +\begin{itemdecl} +constexpr T* operator->() const noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\expects +\tcode{has_value()} is \tcode{true}. + +\pnum +\returns +\tcode{val}. + +\pnum +\remarks +This is a \tcode{const} member function that returns a non-\tcode{const} \tcode{T*}; the constness of \tcode{*this} does not propagate to the referenced object. For deep \tcode{const}, use \tcode{expected}. +\end{itemdescr} + +\indexlibrarymember{operator*}{expected}% +\begin{itemdecl} +constexpr T& operator*() const noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\expects +\tcode{has_value()} is \tcode{true}. + +\pnum +\returns +\tcode{*val}. + +\pnum +\remarks +This is a \tcode{const} member function that returns a non-\tcode{const} \tcode{T&}; the constness of \tcode{*this} does not propagate to the referenced object. For deep \tcode{const}, use \tcode{expected}. +\end{itemdescr} + +\indexlibrarymember{operator bool}{expected}% +\indexlibrarymember{has_value}{expected}% +\begin{itemdecl} +constexpr explicit operator bool() const noexcept; +constexpr bool has_value() const noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\tcode{has_val}. +\end{itemdescr} + +\indexlibrarymember{value}{expected}% +\begin{itemdecl} +constexpr T& value() const&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{is_copy_constructible_v<\exposid{error-value-type}>} is \tcode{true}. + +\pnum +\returns +\tcode{*val} if \tcode{has_value()} is \tcode{true}. + +\pnum +\throws +\tcode{bad_expected_access(as_const(error()))} if \tcode{has_value()} is \tcode{false}. +\end{itemdescr} + +\indexlibrarymember{value}{expected}% +\begin{itemdecl} +constexpr T& value() &&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\tcode{*val} if \tcode{has_value()} is \tcode{true}. + +\pnum +\throws +\tcode{bad_expected_access(std::move(error()))} if \tcode{has_value()} is \tcode{false}. +\end{itemdescr} + +\indexlibrarymember{error}{expected}% +\begin{itemdecl} +constexpr const E& error() const& noexcept; +constexpr E& error() & noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\expects +\tcode{has_value()} is \tcode{false}. + +\pnum +\returns +\tcode{unex.error()}. +\end{itemdescr} + +\indexlibrarymember{error}{expected}% +\begin{itemdecl} +constexpr const E&& error() const&& noexcept; +constexpr E&& error() && noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\expects +\tcode{has_value()} is \tcode{false}. + +\pnum +\returns +\tcode{std::move(unex).error()}. +\end{itemdescr} + +\indexlibrarymember{value_or}{expected}% +\begin{itemdecl} +template> + requires(is_object_v && !is_array_v) +constexpr remove_cv_t value_or(U&& def) const; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{is_convertible_v>} and \tcode{is_convertible_v>} are \tcode{true}. + +\pnum +\returns +\tcode{has_value() ? static_cast>(*val) : static_cast>(std::forward(def))}. The result is an object, never a reference. +\end{itemdescr} + +\indexlibrarymember{error_or}{expected}% +\begin{itemdecl} +template + requires(is_copy_constructible_v>> && + is_convertible_v>>) +constexpr @\exposidnc{error-value-type}@ error_or(G&& def) const&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\tcode{std::forward(def)} if \tcode{has_value()} is \tcode{true}, \tcode{error()} otherwise. The result is an object, never a reference. +\end{itemdescr} + +\indexlibrarymember{error_or}{expected}% +\begin{itemdecl} +template + requires(is_move_constructible_v>> && + is_convertible_v>>) +constexpr @\exposidnc{error-value-type}@ error_or(G&& def) &&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\tcode{std::forward(def)} if \tcode{has_value()} is \tcode{true}, \tcode{std::move(error())} otherwise. The result is an object, never a reference. +\end{itemdescr} + +\rSec3[expected.ref.monadic]{Monadic operations} + +\indexlibrarymember{and_then}{expected}% +\begin{itemdecl} +template + requires is_constructible_v +constexpr auto and_then(F&& f) &; +template + requires is_constructible_v +constexpr auto and_then(F&& f) const&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\mandates +\tcode{remove_cvref_t>} is a specialization of \tcode{expected} and its \tcode{error_type} is the same type as \tcode{E}. + +\pnum +\effects +Equivalent to: \tcode{if (has_value()) return invoke(std::forward(f), *val); else return U(unexpect, error());} where \tcode{U} is \tcode{remove_cvref_t>}. + +\pnum +\remarks +The member templates \tcode{and_then}, \tcode{or_else}, \tcode{transform}, and \tcode{transform_error} behave as specified for the primary template, with one difference: the value is passed to the callable as \tcode{T&} for every ref-qualification of \tcode{*this}. An rvalue \tcode{expected} does not pass its referent as an rvalue; the object referred to is never moved from by these operations. +\end{itemdescr} + +\indexlibrarymember{and_then}{expected}% +\begin{itemdecl} +template + requires is_constructible_v +constexpr auto and_then(F&& f) &&; +template + requires is_constructible_v +constexpr auto and_then(F&& f) const&&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\mandates +\tcode{remove_cvref_t>} is a specialization of \tcode{expected} and its \tcode{error_type} is the same type as \tcode{E}. + +\pnum +\effects +Equivalent to: \tcode{if (has_value()) return invoke(std::forward(f), *val); else return U(unexpect, std::move(error()));} where \tcode{U} is \tcode{remove_cvref_t>}. +\end{itemdescr} + +\indexlibrarymember{or_else}{expected}% +\begin{itemdecl} +template constexpr auto or_else(F&& f) &; +template constexpr auto or_else(F&& f) const&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{remove_cvref_t>} is a specialization of \tcode{expected} and its \tcode{value_type} is the same type as \tcode{T&}. + +\pnum +\effects +Equivalent to: \tcode{if (has_value()) return G(*val); else return invoke(std::forward(f), error());} where \tcode{G} is \tcode{remove_cvref_t>}. +\end{itemdescr} + +\indexlibrarymember{or_else}{expected}% +\begin{itemdecl} +template constexpr auto or_else(F&& f) &&; +template constexpr auto or_else(F&& f) const&&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{remove_cvref_t>} is a specialization of \tcode{expected} and its \tcode{value_type} is the same type as \tcode{T&}. + +\pnum +\effects +Equivalent to: \tcode{if (has_value()) return G(*val); else return invoke(std::forward(f), std::move(error()));} where \tcode{G} is \tcode{remove_cvref_t>}. +\end{itemdescr} + +\indexlibrarymember{transform}{expected}% +\begin{itemdecl} +template + requires is_constructible_v +constexpr auto transform(F&& f) &; +template + requires is_constructible_v +constexpr auto transform(F&& f) const&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\effects +Equivalent to: \tcode{if (!has_value()) return U(unexpect, error()); else return expected(in_place, invoke(std::forward(f), *val));} where \tcode{U2} is \tcode{remove_cv_t>} and \tcode{U} is \tcode{expected}. +\end{itemdescr} + +\indexlibrarymember{transform}{expected}% +\begin{itemdecl} +template + requires is_constructible_v +constexpr auto transform(F&& f) &&; +template + requires is_constructible_v +constexpr auto transform(F&& f) const&&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\effects +Equivalent to: \tcode{if (!has_value()) return U(unexpect, std::move(error())); else return expected(in_place, invoke(std::forward(f), *val));} where \tcode{U2} is \tcode{remove_cv_t>} and \tcode{U} is \tcode{expected}. +\end{itemdescr} + +\indexlibrarymember{transform_error}{expected}% +\begin{itemdecl} +template constexpr auto transform_error(F&& f) &; +template constexpr auto transform_error(F&& f) const&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\begin{itemize} +\item \tcode{is_object_v} is \tcode{true}, +\item \tcode{is_array_v} is \tcode{false}, +\item \tcode{is_same_v>} is \tcode{true}, +\item \tcode{\exposid{is-unexpected-specialization}::value} is \tcode{false}. +\end{itemize} + +\pnum +\effects +Equivalent to: \tcode{if (has_value()) return G(*val); else return expected(unexpect, invoke(std::forward(f), error()));} where \tcode{G2} is \tcode{remove_cv_t>} and \tcode{G} is \tcode{expected}. +\end{itemdescr} + +\indexlibrarymember{transform_error}{expected}% +\begin{itemdecl} +template constexpr auto transform_error(F&& f) &&; +template constexpr auto transform_error(F&& f) const&&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\begin{itemize} +\item \tcode{is_object_v} is \tcode{true}, +\item \tcode{is_array_v} is \tcode{false}, +\item \tcode{is_same_v>} is \tcode{true}, +\item \tcode{\exposid{is-unexpected-specialization}::value} is \tcode{false}. +\end{itemize} + +\pnum +\effects +Equivalent to: \tcode{if (has_value()) return G(*val); else return expected(unexpect, invoke(std::forward(f), std::move(error())));} where \tcode{G2} is \tcode{remove_cv_t>} and \tcode{G} is \tcode{expected}. +\end{itemdescr} + +\rSec3[expected.ref.eq]{Equality operators} + +\indexlibraryglobal{operator==}% +\begin{itemdecl} +template + requires(!is_void_v) +friend constexpr bool operator==(const expected& x, const expected& y); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{!is_void_v} is \tcode{true}. The expression \tcode{*x == *y} is well-formed and its result is convertible to \tcode{bool}. The expression \tcode{x.error() == y.error()} is well-formed and its result is convertible to \tcode{bool}. + +\pnum +\returns +If \tcode{x.has_value() != y.has_value()}, \tcode{false}; otherwise, if \tcode{x.has_value()} is \tcode{true}, \tcode{*x == *y}; otherwise \tcode{x.error() == y.error()}. + +\pnum +\remarks +The equality operators behave as specified for the primary template, comparing referents through \tcode{operator*} and errors through \tcode{error()}. +\end{itemdescr} + +\indexlibraryglobal{operator==}% +\begin{itemdecl} +template + requires(!@\exposidnc{is-expected-specialization}@::value) +friend constexpr bool operator==(const expected& x, const T2& val); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{T2} is not a specialization of \tcode{expected}. The expression \tcode{*x == val} is well-formed and its result is convertible to \tcode{bool}. + +\pnum +\returns +\tcode{x.has_value() && static_cast(*x == val)}. +\end{itemdescr} + +\indexlibraryglobal{operator==}% +\begin{itemdecl} +template +friend constexpr bool operator==(const expected& x, const unexpected& e); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +The expression \tcode{x.error() == e.error()} is well-formed and its result is convertible to \tcode{bool}. + +\pnum +\returns +\tcode{!x.has_value() && static_cast(x.error() == e.error())}. +\end{itemdescr} diff --git a/papers/wording/fragments/expected.unexpected.tex b/papers/wording/fragments/expected.unexpected.tex new file mode 100644 index 0000000..40ce691 --- /dev/null +++ b/papers/wording/fragments/expected.unexpected.tex @@ -0,0 +1,356 @@ +\rSec2[expected.unexpected]{Class template unexpected} + +\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 +}; +\end{codeblock} + +\begin{itemdescr} +\pnum +\mandates +A program that instantiates the definition of \tcode{unexpected} for a non-object type other than an lvalue reference type, an array type, a specialization of \tcode{unexpected}, or a cv-qualified type is ill-formed. + +\pnum +\remarks +Subclause \iref{expected.unexpected} describes the class template \tcode{unexpected} that represents unexpected objects stored in \tcode{expected} objects. +\end{itemdescr} + +\rSec3[expected.un.cons]{Constructors} + +\indexlibraryctor{unexpected}% +\begin{itemdecl} +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); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\begin{itemize} +\item \tcode{is_same_v, unexpected>} is \tcode{false}; and +\item \tcode{is_same_v, in_place_t>} is \tcode{false}; and +\item \tcode{is_constructible_v} is \tcode{true}. +\end{itemize} + +\pnum +\effects +Direct-non-list-initializes \tcode{unex} with \tcode{std::forward(e)}. + +\pnum +\throws +Any exception thrown by the initialization of \tcode{unex}. +\end{itemdescr} + +\indexlibraryctor{unexpected}% +\begin{itemdecl} +template + requires is_constructible_v +constexpr explicit unexpected(in_place_t, Args&&... args) noexcept( + is_nothrow_constructible_v); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\effects +Direct-non-list-initializes \tcode{unex} with \tcode{std::forward(args)...}. + +\pnum +\throws +Any exception thrown by the initialization of \tcode{unex}. +\end{itemdescr} + +\indexlibraryctor{unexpected}% +\begin{itemdecl} +template + requires is_constructible_v&, Args...> +constexpr explicit unexpected( + in_place_t, initializer_list il, + Args&&... args) noexcept(is_nothrow_constructible_v&, + Args...>); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v&, Args...>} is \tcode{true}. + +\pnum +\effects +Direct-non-list-initializes \tcode{unex} with \tcode{il, std::forward(args)...}. + +\pnum +\throws +Any exception thrown by the initialization of \tcode{unex}. +\end{itemdescr} + +\rSec3[expected.un.obs]{Observers} + +\indexlibrarymember{error}{unexpected}% +\begin{itemdecl} +constexpr const E& error() const& noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\tcode{unex}. +\end{itemdescr} + +\indexlibrarymember{error}{unexpected}% +\begin{itemdecl} +constexpr E& error() & noexcept; +\end{itemdecl} + +\indexlibrarymember{error}{unexpected}% +\begin{itemdecl} +constexpr const E&& error() const&& noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\tcode{std::move(unex)}. +\end{itemdescr} + +\indexlibrarymember{error}{unexpected}% +\begin{itemdecl} +constexpr E&& error() && noexcept; +\end{itemdecl} + +\rSec3[expected.un.swap]{Swap} + +\indexlibrarymember{swap}{unexpected}% +\begin{itemdecl} +constexpr void swap(unexpected& other) noexcept(is_nothrow_swappable_v); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{is_swappable_v} is \tcode{true}. + +\pnum +\effects +Equivalent to: \tcode{using std::swap; swap(unex, other.unex);} +\end{itemdescr} + +\indexlibraryglobal{swap}% +\begin{itemdecl} +friend constexpr void swap(unexpected& x, unexpected& y) noexcept(noexcept(x.swap(y))); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_swappable_v} is \tcode{true}. + +\pnum +\effects +Equivalent to \tcode{x.swap(y)}. +\end{itemdescr} + +\rSec3[expected.un.eq]{Equality operator} + +\indexlibraryglobal{operator==}% +\begin{itemdecl} +template +friend constexpr bool operator==(const unexpected& x, const unexpected& y); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +The expression \tcode{x.error() == y.error()} is well-formed and its result is convertible to \tcode{bool}. + +\pnum +\returns +\tcode{x.error() == y.error()}. +\end{itemdescr} + +\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; + + constexpr unexpected& operator=(const unexpected&) = default; + constexpr unexpected& operator=(unexpected&&) = default; + + constexpr E& error() const noexcept; + + constexpr void swap(unexpected& other) noexcept; + + template + friend constexpr bool operator==(const unexpected&, const unexpected&); + + friend constexpr void swap(unexpected& x, unexpected& y) noexcept; + +private: + E* unex; // exposition only +}; +\end{codeblock} + +\begin{itemdescr} +\pnum +\mandates +A program that instantiates the definition of \tcode{unexpected} for an array type or a specialization of \tcode{unexpected} is ill-formed. + +\pnum +\remarks +An object of type \tcode{unexpected} holds a pointer to an object of type \tcode{E}. The referenced object is not owned by the \tcode{unexpected} object. Unlike the primary template, \tcode{E} may be a cv-qualified type. +\end{itemdescr} + +\indexlibraryctor{unexpected}% +\begin{itemdecl} +template + requires(!is_same_v, unexpected> && + !is_same_v, in_place_t> && is_constructible_v && + !reference_constructs_from_temporary_v) +constexpr explicit unexpected(G&& e) noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\begin{itemize} +\item \tcode{is_same_v, unexpected>} is \tcode{false}, +\item \tcode{is_same_v, in_place_t>} is \tcode{false}, +\item \tcode{is_constructible_v} is \tcode{true}, and +\item \tcode{reference_constructs_from_temporary_v} is \tcode{false}. +\end{itemize} + +\pnum +\effects +Initializes \tcode{unex} with \tcode{addressof(static_cast(std::forward(e)))}. + +\pnum +\remarks +A constructor for which \tcode{reference_constructs_from_temporary_v} is \tcode{true} — one that would bind \tcode{E&} to a temporary — is defined as deleted. +\end{itemdescr} + +\indexlibraryctor{unexpected}% +\begin{itemdecl} +template + requires(is_constructible_v && !reference_constructs_from_temporary_v) +constexpr explicit unexpected(in_place_t, G&& e) noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true} and \tcode{reference_constructs_from_temporary_v} is \tcode{false}. + +\pnum +\effects +Initializes \tcode{unex} with \tcode{addressof(static_cast(std::forward(e)))}. +\end{itemdescr} + +\indexlibrarymember{error}{unexpected}% +\begin{itemdecl} +constexpr E& error() const noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\tcode{*unex}. + +\pnum +\remarks +The reference returned is not \tcode{const}-qualified even when \tcode{*this} is \tcode{const}; the constness of the \tcode{unexpected} object does not propagate to the referenced object. +\end{itemdescr} + +\indexlibrarymember{swap}{unexpected}% +\begin{itemdecl} +constexpr void swap(unexpected& other) noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Exchanges \tcode{unex} and \tcode{other.unex}. +\end{itemdescr} + +\indexlibraryglobal{operator==}% +\begin{itemdecl} +template +friend constexpr bool operator==(const unexpected& x, const unexpected& y); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +The expression \tcode{x.error() == y.error()} is well-formed and its result is convertible to \tcode{bool}. + +\pnum +\returns +\tcode{x.error() == y.error()}. +\end{itemdescr} + +\indexlibraryglobal{swap}% +\begin{itemdecl} +friend constexpr void swap(unexpected& x, unexpected& y) noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Equivalent to \tcode{x.swap(y)}. +\end{itemdescr} diff --git a/papers/wording/fragments/expected.void.tex b/papers/wording/fragments/expected.void.tex new file mode 100644 index 0000000..34ca1b5 --- /dev/null +++ b/papers/wording/fragments/expected.void.tex @@ -0,0 +1,1165 @@ +\rSec2[expected.void]{Partial specialization of expected for void types} + +\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 + }; +}; +\end{codeblock} + +\begin{itemdescr} +\pnum +\mandates +A program that instantiates the definition of \tcode{expected} with an \tcode{E} that is not a valid template argument for \tcode{unexpected} is ill-formed. + +\pnum +\remarks +Any object of type \tcode{expected} either represents a value of type \tcode{T}, or contains a value of type \tcode{E} nested within it. Member \tcode{has_val} indicates whether the \tcode{expected} object represents a value of type \tcode{T}. When \tcode{has_value()} is \tcode{false}, the error is \tcode{unex.error()}. +\end{itemdescr} + +\rSec3[expected.void.cons]{Constructors} + +\indexlibraryctor{expected}% +\begin{itemdecl} +constexpr expected() noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\ensures +\tcode{has_value()} is \tcode{true}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +constexpr expected(const expected& rhs) noexcept(is_nothrow_copy_constructible_v); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_copy_constructible_v} is \tcode{true} and \tcode{is_trivially_copy_constructible_v} is \tcode{false}. + +\pnum +\effects +If \tcode{rhs.has_value()} is \tcode{false}, direct-non-list-initializes \tcode{unex} with \tcode{rhs.error()}. + +\pnum +\ensures +\tcode{rhs.has_value() == this->has_value()}. + +\pnum +\throws +Any exception thrown by the initialization of \tcode{unex}. + +\pnum +\remarks +This constructor is defined as deleted unless \tcode{is_copy_constructible_v} is \tcode{true} or \tcode{is_reference_v} is \tcode{true}. This constructor is trivial if \tcode{is_trivially_copy_constructible_v} is \tcode{true}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +constexpr expected(expected&& rhs) noexcept(is_nothrow_move_constructible_v); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_move_constructible_v} is \tcode{true} and \tcode{is_trivially_move_constructible_v} is \tcode{false}. + +\pnum +\effects +If \tcode{rhs.has_value()} is \tcode{false}, direct-non-list-initializes \tcode{unex} with \tcode{std::move(rhs.error())}. + +\pnum +\ensures +\tcode{rhs.has_value()} is unchanged; \tcode{rhs.has_value() == this->has_value()} is \tcode{true}. + +\pnum +\throws +Any exception thrown by the initialization of \tcode{unex}. + +\pnum +\remarks +This constructor is trivial if \tcode{is_trivially_move_constructible_v} is \tcode{true}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +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); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_void_v} is \tcode{true}; and \tcode{is_constructible_v} is \tcode{true}; and \tcode{is_constructible_v, expected&>} is \tcode{false}; and \tcode{is_constructible_v, expected>} is \tcode{false}; and \tcode{is_constructible_v, const expected&>} is \tcode{false}; and \tcode{is_constructible_v, const expected>} is \tcode{false}. + +\pnum +\effects +If \tcode{rhs.has_value()} is \tcode{false}, direct-non-list-initializes \tcode{unex} with \tcode{rhs.error()}. + +\pnum +\ensures +\tcode{rhs.has_value()} is unchanged; \tcode{rhs.has_value() == this->has_value()} is \tcode{true}. + +\pnum +\throws +Any exception thrown by the initialization of \tcode{unex}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +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); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\effects +Direct-non-list-initializes \tcode{unex} with \tcode{e.error()}. + +\pnum +\ensures +\tcode{has_value()} is \tcode{false}. + +\pnum +\throws +Any exception thrown by the initialization of \tcode{unex}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +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; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_reference_v} is \tcode{true}; and \tcode{is_constructible_v} is \tcode{true}; and \tcode{reference_constructs_from_temporary_v} is \tcode{false}. + +\pnum +\effects +Initializes \tcode{unex} with \tcode{e.error()}. + +\pnum +\ensures +\tcode{has_value()} is \tcode{false}. + +\pnum +\remarks +This constructor never throws: the referent is bound, not copied. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + requires(is_reference_v && !is_reference_v) +constexpr expected(const unexpected&); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\remarks +When \tcode{E} is a reference type, an overload taking \tcode{unexpected} for a non-reference \tcode{G} is defined as deleted: the referent would live inside the (possibly temporary) source \tcode{unexpected} object, and binding \tcode{E&} to it would dangle. Use an \tcode{unexpected} holding an external object instead. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +constexpr explicit expected(in_place_t) noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\ensures +\tcode{has_value()} is \tcode{true}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + requires(is_constructible_v && !@\exposidnc{unexpect-dangles-v}@) +constexpr explicit expected(unexpect_t, Args&&... args); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\effects +Direct-non-list-initializes \tcode{unex} with \tcode{std::forward(args)...}. + +\pnum +\ensures +\tcode{has_value()} is \tcode{false}. + +\pnum +\throws +Any exception thrown by the initialization of \tcode{unex}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + requires(@\exposidnc{unexpect-dangles-v}@) +constexpr expected(unexpect_t, Args&&...); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\remarks +When \tcode{E} is a reference type, an overload with the same parameter types is defined as deleted if the single argument would bind \tcode{E&} to a temporary, or if it is otherwise not usable to construct \tcode{E}. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + requires(!is_reference_v && is_constructible_v&, Args...>) +constexpr explicit expected(unexpect_t, initializer_list il, Args&&... args); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_reference_v} is \tcode{false}, and \tcode{is_constructible_v&, Args...>} is \tcode{true}. + +\pnum +\effects +Direct-non-list-initializes \tcode{unex} with \tcode{il, std::forward(args)...}. + +\pnum +\ensures +\tcode{has_value()} is \tcode{false}. + +\pnum +\throws +Any exception thrown by the initialization of \tcode{unex}. + +\pnum +\remarks +An overload with the same parameter types is defined as deleted when \tcode{E} is an lvalue reference type. An initializer list cannot provide the required long-lived error referent. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +template + requires is_reference_v +constexpr expected(unexpect_t, initializer_list, Args&&...); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\remarks +An overload with the same parameter types is defined as deleted when \tcode{E} is an lvalue reference type. An initializer list cannot provide the required long-lived error referent. +\end{itemdescr} + +\indexlibraryctor{expected}% +\begin{itemdecl} +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); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_convertible_v} is \tcode{true}; and \tcode{reference_constructs_from_temporary_v} is \tcode{false}. + +\pnum +\effects +If \tcode{rhs.has_value()} is \tcode{false}, direct-non-list-initializes \tcode{unex} with \tcode{rhs.error()}. + +\pnum +\ensures +\tcode{rhs.has_value()} is unchanged; \tcode{rhs.has_value() == this->has_value()} is \tcode{true}. + +\pnum +\remarks +This constructor never throws: the referent is bound, not copied. It participates in overload resolution only when \tcode{E} is a reference type, mirroring the \tcode{unexpected} reference-\tcode{E} path above. +\end{itemdescr} + +\rSec3[expected.void.dtor]{Destructor} + +\indexlibrarydtor{expected}% +\begin{itemdecl} +constexpr ~expected(); +\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}. + +\pnum +\remarks +If \tcode{is_trivially_destructible_v} is \tcode{true}, then this destructor is a trivial destructor. +\end{itemdescr} + +\rSec3[expected.void.assign]{Assignment} + +\indexlibrarymember{operator=}{expected}% +\begin{itemdecl} +constexpr expected& operator=(const expected& rhs) noexcept( + is_nothrow_copy_constructible_v && is_nothrow_copy_assignable_v); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_reference_v || (is_copy_constructible_v && is_copy_assignable_v)} is \tcode{true} and \tcode{(is_trivially_copy_constructible_v && is_trivially_copy_assignable_v && + is_trivially_destructible_v)} is \tcode{false}. + +\pnum +\effects +If \tcode{this->has_value() && rhs.has_value()}, no effects. Otherwise, if \tcode{this->has_value()}, equivalent to: \tcode{construct_at(addressof(unex), rhs.unex); has_val = false;} Otherwise, if \tcode{rhs.has_value()}, destroys \tcode{unex} and sets \tcode{has_val} to \tcode{true}. Otherwise, equivalent to \tcode{unex = rhs.unex}. + +\pnum +\returns +\tcode{*this}. + +\pnum +\remarks +This operator is defined as deleted unless \tcode{is_copy_assignable_v} is \tcode{true} or \tcode{is_reference_v} is \tcode{true} and \tcode{is_copy_constructible_v} is \tcode{true} or \tcode{is_reference_v} is \tcode{true}. This operator is trivial if \tcode{is_trivially_copy_constructible_v}, \tcode{is_trivially_copy_assignable_v}, and \tcode{is_trivially_destructible_v} are all \tcode{true}. +\end{itemdescr} + +\indexlibrarymember{operator=}{expected}% +\begin{itemdecl} +constexpr expected& operator=(expected&& rhs) noexcept( + is_nothrow_move_constructible_v && is_nothrow_move_assignable_v); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_reference_v || (is_move_constructible_v && is_move_assignable_v)} is \tcode{true} and \tcode{(is_trivially_move_constructible_v && is_trivially_move_assignable_v && + is_trivially_destructible_v)} is \tcode{false}. + +\pnum +\effects +If \tcode{this->has_value() && rhs.has_value()}, no effects. Otherwise, if \tcode{this->has_value()}, equivalent to: \tcode{construct_at(addressof(unex), std::move(rhs.unex)); has_val = false;} Otherwise, if \tcode{rhs.has_value()}, destroys \tcode{unex} and sets \tcode{has_val} to \tcode{true}. Otherwise, equivalent to \tcode{unex = std::move(rhs.unex)}. + +\pnum +\returns +\tcode{*this}. + +\pnum +\remarks +The exception specification is equivalent to \tcode{is_nothrow_move_constructible_v && is_nothrow_move_assignable_v}. This operator is trivial if \tcode{is_trivially_move_constructible_v}, \tcode{is_trivially_move_assignable_v}, and \tcode{is_trivially_destructible_v} are all \tcode{true}. +\end{itemdescr} + +\indexlibrarymember{operator=}{expected}% +\begin{itemdecl} +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); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true} and \tcode{is_assignable_v} is \tcode{true}. + +\pnum +\effects +If \tcode{has_value()} is \tcode{true}, equivalent to: \tcode{construct_at(addressof(unex), e.error()); has_val = false;} Otherwise, equivalent to: \tcode{unex = unexpected(e.error());} + +\pnum +\returns +\tcode{*this}. +\end{itemdescr} + +\indexlibrarymember{operator=}{expected}% +\begin{itemdecl} +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); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_reference_v} is \tcode{true}; and \tcode{is_constructible_v} is \tcode{true}; and \tcode{reference_constructs_from_temporary_v} is \tcode{false}. + +\pnum +\effects +Rebinds \tcode{unex} to refer to the same object as \tcode{e.error()}. + +\pnum +\ensures +\tcode{has_value()} is \tcode{false}. + +\pnum +\returns +\tcode{*this}. + +\pnum +\remarks +This operator never throws: the referent is bound, not copied. +\end{itemdescr} + +\indexlibrarymember{operator=}{expected}% +\begin{itemdecl} +template + requires(is_reference_v && !is_reference_v) +constexpr expected& operator=(const unexpected&); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\remarks +When \tcode{E} is a reference type, an overload taking \tcode{unexpected} for a non-reference \tcode{G} is defined as deleted: it would rebind \tcode{unex} to \tcode{unexpected}'s temporary storage. +\end{itemdescr} + +\indexlibrarymember{emplace}{expected}% +\begin{itemdecl} +constexpr void emplace() noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +If \tcode{has_value()} is \tcode{false}, destroys \tcode{unex} and sets \tcode{has_val} to \tcode{true}. +\end{itemdescr} + +\rSec3[expected.void.swap]{Swap} + +\indexlibrarymember{swap}{expected}% +\begin{itemdecl} +constexpr void swap(expected& rhs) noexcept(is_nothrow_move_constructible_v && + (is_reference_v || + is_nothrow_swappable_v)); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_reference_v || is_swappable_v} is \tcode{true} and \tcode{is_move_constructible_v} is \tcode{true}. + +\pnum +\effects +If \tcode{this->has_value()} and \tcode{rhs.has_value()}, no effects. If neither \tcode{*this} nor \tcode{rhs} contains a value, equivalent to \tcode{using std::swap; swap(unex, rhs.unex);}. If \tcode{rhs.has_value()} is \tcode{false} and \tcode{this->has_value()} is \tcode{true}, initializes \tcode{rhs.unex} from \tcode{std::move(unex)}, destroys \tcode{unex}, and leaves \tcode{has_value()} \tcode{false} and \tcode{rhs.has_value()} \tcode{true}. If \tcode{rhs.has_value()} is \tcode{true} and \tcode{this->has_value()} is \tcode{false}, equivalent to \tcode{rhs.swap(*this)}. + +\pnum +\throws +Any exception thrown by the expressions in the Effects. + +\pnum +\remarks +The exception specification is equivalent to \tcode{is_nothrow_move_constructible_v && (is_reference_v || is_nothrow_swappable_v)}. +\end{itemdescr} + +\indexlibraryglobal{swap}% +\begin{itemdecl} +friend constexpr void swap(expected& x, expected& y) noexcept(noexcept(x.swap(y))); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_reference_v || is_swappable_v} is \tcode{true} and \tcode{is_move_constructible_v} is \tcode{true}. + +\pnum +\effects +Equivalent to \tcode{x.swap(y)}. +\end{itemdescr} + +\rSec3[expected.void.obs]{Observers} + +\indexlibrarymember{operator bool}{expected}% +\indexlibrarymember{has_value}{expected}% +\begin{itemdecl} +constexpr explicit operator bool() const noexcept; +constexpr bool has_value() const noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\returns +\tcode{has_val}. +\end{itemdescr} + +\indexlibrarymember{operator*}{expected}% +\begin{itemdecl} +constexpr void operator*() const noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\hardexpects +\tcode{has_value()} is \tcode{true}. +\end{itemdescr} + +\indexlibrarymember{value}{expected}% +\begin{itemdecl} +constexpr void value() const&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{is_copy_constructible_v} is \tcode{true}. + +\pnum +\throws +\tcode{bad_expected_access(error())} if \tcode{has_value()} is \tcode{false}. +\end{itemdescr} + +\indexlibrarymember{value}{expected}% +\begin{itemdecl} +constexpr void value() &&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{is_copy_constructible_v} is \tcode{true} and \tcode{is_move_constructible_v} is \tcode{true}. + +\pnum +\throws +\tcode{bad_expected_access(std::move(error()))} if \tcode{has_value()} is \tcode{false}. +\end{itemdescr} + +\indexlibrarymember{error}{expected}% +\begin{itemdecl} +constexpr const E& error() const& noexcept; +constexpr E& error() & noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\hardexpects +\tcode{has_value()} is \tcode{false}. + +\pnum +\returns +\tcode{unex.error()}. +\end{itemdescr} + +\indexlibrarymember{error}{expected}% +\begin{itemdecl} +constexpr const E&& error() const&& noexcept; +constexpr E&& error() && noexcept; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\hardexpects +\tcode{has_value()} is \tcode{false}. + +\pnum +\returns +\tcode{std::move(unex).error()}. +\end{itemdescr} + +\indexlibrarymember{error_or}{expected}% +\begin{itemdecl} +template + requires(is_copy_constructible_v>> && + is_convertible_v>>) +constexpr @\exposidnc{error-value-type}@ error_or(G&& def) const&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{is_copy_constructible_v} is \tcode{true} and \tcode{is_convertible_v} is \tcode{true}. + +\pnum +\returns +\tcode{std::forward(def)} if \tcode{has_value()} is \tcode{true}, \tcode{error()} otherwise. +\end{itemdescr} + +\indexlibrarymember{error_or}{expected}% +\begin{itemdecl} +template + requires(is_move_constructible_v>> && + is_convertible_v>>) +constexpr @\exposidnc{error-value-type}@ error_or(G&& def) &&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{is_move_constructible_v} is \tcode{true} and \tcode{is_convertible_v} is \tcode{true}. + +\pnum +\returns +\tcode{std::forward(def)} if \tcode{has_value()} is \tcode{true}, \tcode{std::move(error())} otherwise. +\end{itemdescr} + +\indexlibrarymember{value_or}{expected}% +\begin{itemdecl} +template + requires is_reference_v +constexpr void value_or(U&&) const; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\remarks +\tcode{expected} has no \tcode{value_or} member: there is no value to fall back from. This overload exists only to give a clear diagnostic when \tcode{E} is a reference type, and is defined as deleted. +\end{itemdescr} + +\rSec3[expected.void.monadic]{Monadic operations} + +\indexlibrarymember{and_then}{expected}% +\begin{itemdecl} +template + requires is_constructible_v +constexpr auto and_then(F&& f) &; +template + requires is_constructible_v +constexpr auto and_then(F&& f) const&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\mandates +\tcode{remove_cvref_t>} is a specialization of \tcode{expected} and its \tcode{error_type} is the same type as \tcode{E}. + +\pnum +\effects +Equivalent to: \tcode{if (has_value()) return invoke(std::forward(f)); else return U(unexpect, error());} where \tcode{U} is \tcode{remove_cvref_t>}. +\end{itemdescr} + +\indexlibrarymember{and_then}{expected}% +\begin{itemdecl} +template + requires is_constructible_v +constexpr auto and_then(F&& f) &&; +template + requires is_constructible_v +constexpr auto and_then(F&& f) const&&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\mandates +\tcode{remove_cvref_t>} is a specialization of \tcode{expected} and its \tcode{error_type} is the same type as \tcode{E}. + +\pnum +\effects +Equivalent to: \tcode{if (has_value()) return invoke(std::forward(f)); else return U(unexpect, std::move(error()));} where \tcode{U} is \tcode{remove_cvref_t>}. +\end{itemdescr} + +\indexlibrarymember{or_else}{expected}% +\begin{itemdecl} +template constexpr auto or_else(F&& f) &; +template constexpr auto or_else(F&& f) const&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{remove_cvref_t>} is a specialization of \tcode{expected} and its \tcode{value_type} is the same type as \tcode{T}. + +\pnum +\effects +Equivalent to: \tcode{if (has_value()) return G(); else return invoke(std::forward(f), error());} where \tcode{G} is \tcode{remove_cvref_t>}. +\end{itemdescr} + +\indexlibrarymember{or_else}{expected}% +\begin{itemdecl} +template constexpr auto or_else(F&& f) &&; +template constexpr auto or_else(F&& f) const&&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{remove_cvref_t>} is a specialization of \tcode{expected} and its \tcode{value_type} is the same type as \tcode{T}. + +\pnum +\effects +Equivalent to: \tcode{if (has_value()) return G(); else return invoke(std::forward(f), std::move(error()));} where \tcode{G} is \tcode{remove_cvref_t>}. +\end{itemdescr} + +\indexlibrarymember{transform}{expected}% +\begin{itemdecl} +template + requires is_constructible_v +constexpr auto transform(F&& f) &; +template + requires is_constructible_v +constexpr auto transform(F&& f) const&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\mandates +\tcode{U} is a valid value type for \tcode{expected}, where \tcode{U} is \tcode{remove_cv_t>}. + +\pnum +\effects +If \tcode{has_value()} is \tcode{false}, returns \tcode{expected(unexpect, error())}. Otherwise, if \tcode{is_void_v} is \tcode{false}, returns an \tcode{expected} object whose \tcode{has_val} member is \tcode{true} and \tcode{val} member is direct-non-list-initialized with \tcode{invoke(std::forward(f))}. Otherwise, evaluates \tcode{invoke(std::forward(f))} and then returns \tcode{expected()}. +\end{itemdescr} + +\indexlibrarymember{transform}{expected}% +\begin{itemdecl} +template + requires is_constructible_v +constexpr auto transform(F&& f) &&; +template + requires is_constructible_v +constexpr auto transform(F&& f) const&&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\constraints +\tcode{is_constructible_v} is \tcode{true}. + +\pnum +\mandates +\tcode{U} is a valid value type for \tcode{expected}, where \tcode{U} is \tcode{remove_cv_t>}. + +\pnum +\effects +If \tcode{has_value()} is \tcode{false}, returns \tcode{expected(unexpect, std::move(error()))}. Otherwise, if \tcode{is_void_v} is \tcode{false}, returns an \tcode{expected} object whose \tcode{has_val} member is \tcode{true} and \tcode{val} member is direct-non-list-initialized with \tcode{invoke(std::forward(f))}. Otherwise, evaluates \tcode{invoke(std::forward(f))} and then returns \tcode{expected()}. +\end{itemdescr} + +\indexlibrarymember{transform_error}{expected}% +\begin{itemdecl} +template constexpr auto transform_error(F&& f) &; +template constexpr auto transform_error(F&& f) const&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{G} is a valid template argument for \tcode{unexpected} and the declaration \tcode{G g(invoke(std::forward(f), error()));} is well-formed, where \tcode{G} is \tcode{remove_cv_t>}. + +\pnum +\returns +If \tcode{has_value()} is \tcode{true}, \tcode{expected()}; otherwise, an \tcode{expected} object whose \tcode{has_val} member is \tcode{false} and \tcode{unex} member is direct-non-list-initialized with \tcode{invoke(std::forward(f), error())}. +\end{itemdescr} + +\indexlibrarymember{transform_error}{expected}% +\begin{itemdecl} +template constexpr auto transform_error(F&& f) &&; +template constexpr auto transform_error(F&& f) const&&; +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{G} is a valid template argument for \tcode{unexpected} and the declaration \tcode{G g(invoke(std::forward(f), std::move(error())));} is well-formed, where \tcode{G} is \tcode{remove_cv_t>}. + +\pnum +\returns +If \tcode{has_value()} is \tcode{true}, \tcode{expected()}; otherwise, an \tcode{expected} object whose \tcode{has_val} member is \tcode{false} and \tcode{unex} member is direct-non-list-initialized with \tcode{invoke(std::forward(f), std::move(error()))}. +\end{itemdescr} + +\rSec3[expected.void.eq]{Equality operators} + +\indexlibraryglobal{operator==}% +\begin{itemdecl} +template + requires is_void_v +friend constexpr bool operator==(const expected& x, const expected& y); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +\tcode{is_void_v} is \tcode{true}. The expression \tcode{x.error() == y.error()} is well-formed and its result is convertible to \tcode{bool}. + +\pnum +\returns +If \tcode{x.has_value() != y.has_value()}, \tcode{false}; otherwise, if \tcode{x.has_value()} is \tcode{true}, \tcode{true}; otherwise \tcode{x.error() == y.error()}. +\end{itemdescr} + +\indexlibraryglobal{operator==}% +\begin{itemdecl} +template +friend constexpr bool operator==(const expected& x, const unexpected& e); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\mandates +The expression \tcode{x.error() == e.error()} is well-formed and its result is convertible to \tcode{bool}. + +\pnum +\returns +\tcode{!x.has_value() && static_cast(x.error() == e.error())}. +\end{itemdescr} diff --git a/papers/wording/preamble.tex b/papers/wording/preamble.tex new file mode 100644 index 0000000..33fb0c7 --- /dev/null +++ b/papers/wording/preamble.tex @@ -0,0 +1,18 @@ +% papers/wording/expected.tex -*-LaTeX-*- +% SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +% +% Generated from the annotated headers in include/beman/expected/ via specgen. +% Do not edit by hand: change a header's //! docblocks and run `make wording` +% instead. This comment comes from papers/wording/preamble.tex, which is the +% one part of this file a person writes. +% +% --base-section-depth 2 puts each \rSec marker at the level the draft +% writes it at: [expected.unexpected] and its siblings are \rSec2, their +% subclauses \rSec3, numbering 22.8.3, 22.8.3.1, ... just as in +% source/utilities.tex. This file is therefore everything that sits +% *inside* the existing \rSec1[expected]{Expected objects} in the draft's +% source/utilities.tex, in clause order, ready to replace the current +% [expected.unexpected] through [expected.void] subclauses and add the new +% [expected.ref] one after them -- no \rSec1[expected] wrapper and no +% \input directives. [expected.general] and [expected.syn] are prose, not +% generated from any one declaration; see papers/expected-new.tex. diff --git a/tests/beman/expected/CMakeLists.txt b/tests/beman/expected/CMakeLists.txt index f3368f2..28ad956 100644 --- a/tests/beman/expected/CMakeLists.txt +++ b/tests/beman/expected/CMakeLists.txt @@ -61,7 +61,7 @@ target_link_libraries( beman.expected.tests.expected PRIVATE beman::expected Catch2::Catch2WithMain ) -catch_discover_tests(beman.expected.tests.expected) +catch_discover_tests(beman.expected.tests.expected DISCOVERY_MODE PRE_TEST) if(BEMAN_EXPECTED_USE_MODULES) add_library(beman.expected.tests.module_parity OBJECT) @@ -111,6 +111,7 @@ if(NOT BEMAN_EXPECTED_USING_LIBCXX) beman.expected.tests.expected.std TEST_PREFIX "std." PROPERTIES LABELS std + DISCOVERY_MODE PRE_TEST ) endif() @@ -136,7 +137,10 @@ if(NOT BEMAN_EXPECTED_USING_LIBCXX) beman.expected.tests.equivalence PRIVATE beman::expected Catch2::Catch2WithMain ) - catch_discover_tests(beman.expected.tests.equivalence) + catch_discover_tests( + beman.expected.tests.equivalence + DISCOVERY_MODE PRE_TEST + ) endif() # ============================================================================= @@ -434,4 +438,4 @@ target_compile_definitions( beman.expected.tests.hardened PRIVATE BEMAN_EXPECTED_HARDENED ) -catch_discover_tests(beman.expected.tests.hardened) +catch_discover_tests(beman.expected.tests.hardened DISCOVERY_MODE PRE_TEST)