From 83723eabc3eef3227550f51e9f108da396c12c83 Mon Sep 17 00:00:00 2001 From: Steve Downey Date: Mon, 7 Sep 2026 21:36:32 -0400 Subject: [PATCH 01/10] fix: defer Catch2 test discovery to ctest time to unblock CodeQL build catch_discover_tests defaults to DISCOVERY_MODE POST_BUILD, which runs the freshly-linked (ASan-instrumented) test binaries during the ninja build itself to enumerate their test cases. Under the CodeQL Advanced workflow, CodeQL's build tracer injects its own LD_PRELOAD ahead of the ASan runtime, and ASan aborts immediately with "ASan runtime does not come first in initial library list", failing the build before analysis can run. Switching to DISCOVERY_MODE PRE_TEST moves the enumeration step to ctest invocation time instead of build time, so no instrumented binary runs while CodeQL is tracing the build. Verified locally: gcc-debug preset builds cleanly and `ctest` still discovers and passes all 1178 tests. --- tests/beman/expected/CMakeLists.txt | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) 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) From 85e6712841a294bd721d9a2fbec2020f1d2f24de Mon Sep 17 00:00:00 2001 From: Steve Downey Date: Tue, 8 Sep 2026 09:15:04 -0400 Subject: [PATCH 02/10] docs: annotate headers with specgen wording docblocks Add //! docblocks (Effects, Constraints, Mandates, Returns, Throws, Remarks) to every declaration in unexpected.hpp, bad_expected_access.hpp, and expected.hpp, sourced from the real standard text for the already-standardized members and from papers/expected-new.tex for the expected-over-references additions (unexpected, expected). specgen generate --validate passes cleanly on all three headers, so the headers are now a source of truth specgen can turn into wording directly. --- .../beman/expected/bad_expected_access.hpp | 16 + include/beman/expected/expected.hpp | 1206 ++++++++++++++++- include/beman/expected/unexpected.hpp | 138 +- 3 files changed, 1298 insertions(+), 62 deletions(-) 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..1190d56 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,27 @@ 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} + +//! \constraints `is_swappable_v` is `true` and (`is_reference_v` or +//! `is_swappable_v`) is `true`, and `is_move_constructible_v && +//! is_move_constructible_v` is `true`, and +//! `is_nothrow_move_constructible_v || +//! is_nothrow_move_constructible_v` is `true`. +//! \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 +1236,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 +1250,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 +1260,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 +1272,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 +1282,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 +1294,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 +1304,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 +1330,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 +1339,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 +1359,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 +1374,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 +1386,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 +1396,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 +1408,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 +1418,9 @@ constexpr E&& expected::error() && noexcept { return std::move(unex_).error(); } +//! \mandates `is_copy_constructible_v` is `true` and +//! `is_convertible_v` is `true`. +//! \returns `has_value() ? **this : static_cast(std::forward(def))`. template template constexpr T expected::value_or(U&& def) const& { @@ -1101,6 +1431,10 @@ constexpr T expected::value_or(U&& def) const& { return static_cast(std::forward(def)); } +//! \mandates `is_move_constructible_v` is `true` and +//! `is_convertible_v` is `true`. +//! \returns `has_value() ? std::move(**this) : +//! static_cast(std::forward(def))`. template template constexpr T expected::value_or(U&& def) && { @@ -1111,6 +1445,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 +1459,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 +1474,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 +1498,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 +1522,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 +1537,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 +1552,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 +1573,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 +1596,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 +1610,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 +1624,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 +1656,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 +1689,7 @@ constexpr auto expected::transform(F&& f) && { } } +//! \also monadic-transform-lval template template requires std::is_constructible_v @@ -1318,6 +1715,7 @@ constexpr auto expected::transform(F&& f) const& { } } +//! \also monadic-transform-rval template template requires std::is_constructible_v @@ -1343,6 +1741,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 +1762,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 +1785,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 +1801,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 +1817,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 +1844,7 @@ class expected { "E must not be an unexpected specialization"); private: + //! \expos using error_value_type = std::remove_cv_t>; public: @@ -1436,6 +1864,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 +1873,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 +1924,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 +1952,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 +1965,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 +1978,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 +2006,8 @@ class expected { // [expected.void.dtor] Destructor // ------------------------------------------------------------------------- + //! \at expected.void.dtor + //! \merge constexpr ~expected() requires std::is_trivially_destructible_v = default; @@ -1565,6 +2020,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 +2035,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 +2070,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 +2097,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 +2135,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 +2197,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 +2213,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 +2259,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 +2274,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 +2298,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 +2311,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 +2323,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 +2331,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 +2347,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 +2356,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 +2368,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 +2384,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 +2404,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 +2415,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 +2429,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 +2466,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 +2500,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 +2520,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 +2536,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 +2555,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 +2566,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 +2577,21 @@ constexpr void expected::emplace() noexcept { } // ============================================================================= -// [expected.void.swap] Out-of-line swap definition -// ============================================================================= - +// \rSec3[expected.void.swap]{Swap} + +//! \constraints `is_swappable_v` is `true` and +//! `is_move_constructible_v` is `true`. +//! \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 +2614,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 +2638,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 +2647,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 +2659,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 +2671,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 +2681,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 +2693,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 +2703,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 +2717,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 +2731,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 +2754,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 +2776,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 +2791,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 +2806,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 +2825,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 +2844,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 +2857,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 +2870,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 +2905,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 +2941,7 @@ constexpr auto expected::transform(F&& f) && { } } +//! \also void-monadic-transform-lval template template requires std::is_constructible_v @@ -2276,6 +2967,7 @@ constexpr auto expected::transform(F&& f) const& { } } +//! \also void-monadic-transform-rval template template requires std::is_constructible_v @@ -2301,6 +2993,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 +3016,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 +3039,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 +3054,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 +3069,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 +3102,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 +3117,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 +3196,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 +3256,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 +3281,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 +3306,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 +3319,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); @@ -2536,6 +3338,18 @@ class expected { // Assignment (rebind semantics) // ------------------------------------------------------------------------- + //! \at expected.ref.assign + //! \remarks Assignment rebinds: assigning to an `expected` that + //! holds a value changes which object it refers to. It never assigns + //! through to the referent. + //! \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 trivial. // Copy assignment (trivial path) constexpr expected& operator=(const expected&) requires(std::is_trivially_copy_constructible_v && std::is_trivially_copy_assignable_v && @@ -2543,6 +3357,17 @@ class expected { = 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 +3375,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 +3408,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 +3457,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 +3490,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 +3600,17 @@ class expected { // Equality operators (hidden friends) // ------------------------------------------------------------------------- + //! \at expected.ref.eq + //! \remarks The equality operators behave as specified for the primary + //! template, comparing referents through `operator*` and errors + //! through `error()`. + //! \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) { @@ -2730,29 +3621,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 +3672,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 +3683,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 +3704,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 +3718,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 +3739,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 +3753,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 +3764,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 +3772,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 +3787,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 +3796,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 +3807,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 +3822,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 +3835,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 +3871,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 +3895,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 +3914,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 +3930,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 +3950,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 +3961,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 +3980,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 +4013,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 +4029,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 +4043,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 +4067,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 +4083,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 +4095,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 +4105,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 +4117,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 +4127,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 +4144,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 +4156,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 +4169,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} + +//! \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. +//! \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>`. template template requires std::is_constructible_v @@ -3159,6 +4198,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 +4221,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 +4236,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 +4251,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 +4270,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 +4289,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 +4302,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 +4315,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 +4346,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 +4378,7 @@ constexpr auto expected::transform(F&& f) && { } } +//! \also ref-monadic-transform-lval template template requires std::is_constructible_v @@ -3324,6 +4404,7 @@ constexpr auto expected::transform(F&& f) const& { } } +//! \also ref-monadic-transform-rval template template requires std::is_constructible_v @@ -3349,6 +4430,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 +4449,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 +4469,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 +4484,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 +4499,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..a3f1e0b 100644 --- a/include/beman/expected/unexpected.hpp +++ b/include/beman/expected/unexpected.hpp @@ -25,9 +25,11 @@ namespace beman { namespace expected { // [expected.unexpect] +//! \omit struct unexpect_t { explicit unexpect_t() = default; }; +//! \omit 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 +//! \expos using std::reference_constructs_from_temporary_v; #elif __has_builtin(__reference_constructs_from_temporary) +//! \expos 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_; }; From 38d55323bd5bcb747892de5442eb945d1d2c92a1 Mon Sep 17 00:00:00 2001 From: Steve Downey Date: Tue, 8 Sep 2026 09:15:22 -0400 Subject: [PATCH 03/10] docs: generate [expected] wording from the annotated headers Add papers/wording/generate.sh (wired up as `make wording`), which runs specgen against the three headers and assembles the result two ways: - papers/wording/fragments/*.tex: one file per top-level clause, for \input into a standalone paper (specgen numbers a fragment's \rSec markers one level deeper than written, so a paper's own \rSec1[expected]{Expected objects} supplies the level these assume). - papers/wording/expected.tex: the same content concatenated in real standard clause order, at the draft's own absolute numbering, with no \rSec2[expected] wrapper and no \input directives -- the basis for a patch to source/utilities.tex in the actual C++ working draft (github.com/cplusplus/draft), where [expected.general] and [expected.syn] are untouched and only the subclauses from [expected.unexpected] on are replaced/extended. [expected.general] and [expected.syn] are prose, not generated from any one declaration; they stay hand-authored in papers/expected-new.tex. --- Makefile | 4 + papers/wording/README.md | 39 + papers/wording/expected.tex | 4679 +++++++++++++++++++++++ papers/wording/fragments/bad-void.tex | 21 + papers/wording/fragments/bad.tex | 100 + papers/wording/fragments/object.tex | 1673 ++++++++ papers/wording/fragments/ref.tex | 1340 +++++++ papers/wording/fragments/unexpected.tex | 356 ++ papers/wording/fragments/void.tex | 1165 ++++++ papers/wording/generate.sh | 94 + 10 files changed, 9471 insertions(+) create mode 100644 papers/wording/README.md create mode 100644 papers/wording/expected.tex create mode 100644 papers/wording/fragments/bad-void.tex create mode 100644 papers/wording/fragments/bad.tex create mode 100644 papers/wording/fragments/object.tex create mode 100644 papers/wording/fragments/ref.tex create mode 100644 papers/wording/fragments/unexpected.tex create mode 100644 papers/wording/fragments/void.tex create mode 100755 papers/wording/generate.sh diff --git a/Makefile b/Makefile index eb40c92..1b41033 100755 --- a/Makefile +++ b/Makefile @@ -157,6 +157,10 @@ env: papers: $(MAKE) -C papers papers +.PHONY: wording +wording: ## Regenerate papers/wording/ from the annotated headers via specgen + papers/wording/generate.sh + .DEFAULT: $(_build_path)/CMakeCache.txt ## Other targets passed through to cmake $(CMAKE) --build $(_build_path) --config $(CONFIG) --target $@ -- -k 0 diff --git a/papers/wording/README.md b/papers/wording/README.md new file mode 100644 index 0000000..6b39c2f --- /dev/null +++ b/papers/wording/README.md @@ -0,0 +1,39 @@ +# 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). Regenerate after editing a +header's docblocks with: + +```sh +make wording +``` + +(equivalently: `papers/wording/generate.sh`, with `specgen` on `PATH`). Don't +hand-edit the files here; the header comments are the source of truth. + +## `expected.tex` + +All the generated subclauses concatenated in real standard order — +`[expected.unexpected]` (including the new `[expected.un.ref]`) through +`[expected.ref.eq]`. `specgen` numbers a fragment's `\rSec` markers one level +deeper than written in the header, which lines up with the draft's own +absolute numbering (`22.8.3`, `22.8.3.1`, ...). This file is everything that +sits *inside* the existing `\rSec2[expected]{Expected objects}` in +[the draft](https://github.com/cplusplus/draft)'s `source/utilities.tex` — no +wrapper `\rSec2[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. + +## `fragments/` + +The same content, split one file per top-level clause +(`unexpected.tex`, `bad.tex`, `bad-void.tex`, `object.tex`, `void.tex`, +`ref.tex`) — for `\input` into a standalone paper's own document, where the +enclosing `\rSec1[expected]{Expected objects}` (see `papers/expected-new.tex`) +supplies the level that `expected.tex` above assumes already exists. diff --git a/papers/wording/expected.tex b/papers/wording/expected.tex new file mode 100644 index 0000000..a178a5a --- /dev/null +++ b/papers/wording/expected.tex @@ -0,0 +1,4679 @@ +% papers/wording/expected.tex -*-LaTeX-*- +% SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +% +% Generated by papers/wording/generate.sh from the annotated headers in +% include/beman/expected/ via specgen. Do not edit by hand: re-run +% generate.sh after changing a header's //! docblocks instead. +% +% specgen numbers each fragment's \rSec markers one level deeper than +% written in the header ([expected.unexpected] etc. render as \rSec3, their +% subclauses as \rSec4), matching the real standard's absolute numbering +% (22.8.3, 22.8.3.1, ...) directly. This file is therefore everything that +% sits *inside* the existing \rSec2[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 \rSec2[expected] wrapper and +% no \input directives. [expected.general] and [expected.syn] are prose, +% not generated from any one declaration; see papers/expected-new.tex. + +\rSec3[expected.unexpected]{Class template unexpected} + +\rSec4[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} + +\rSec4[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} + +\rSec4[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} + +\rSec4[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} + +\rSec4[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} + +\rSec4[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} + +\rSec3[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} + +\rSec3[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} + +\rSec3[expected.expected]{Class template expected} + +\rSec4[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} + +\rSec4[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} + +\rSec4[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} + +\rSec4[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} + +\rSec4[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 +\tcode{is_swappable_v} is \tcode{true} and (\tcode{is_reference_v} or \tcode{is_swappable_v}) is \tcode{true}, and \tcode{is_move_constructible_v && is_move_constructible_v} is \tcode{true}, and \tcode{is_nothrow_move_constructible_v || is_nothrow_move_constructible_v} is \tcode{true}. + +\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} + +\rSec4[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} + +\rSec4[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} + +\rSec4[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} + +\rSec3[expected.void]{Partial specialization of expected for void types} + +\rSec4[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} + +\rSec4[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} + +\rSec4[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} + +\rSec4[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} + +\rSec4[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_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} + +\rSec4[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} + +\rSec4[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} + +\rSec4[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} + +\rSec3[expected.ref]{Partial specialization of expected for reference types} + +\rSec4[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) + // ------------------------------------------------------------------------- + + 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} + +\rSec4[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} + +\rSec4[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} + +\rSec4[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. + +\pnum +This operator is trivial. Copy assignment (trivial path) +\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} + +\rSec4[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} + +\rSec4[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} + +\rSec4[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} + +\rSec4[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/bad-void.tex b/papers/wording/fragments/bad-void.tex new file mode 100644 index 0000000..e630496 --- /dev/null +++ b/papers/wording/fragments/bad-void.tex @@ -0,0 +1,21 @@ +\rSec3[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/bad.tex b/papers/wording/fragments/bad.tex new file mode 100644 index 0000000..c084bd4 --- /dev/null +++ b/papers/wording/fragments/bad.tex @@ -0,0 +1,100 @@ +\rSec3[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/object.tex b/papers/wording/fragments/object.tex new file mode 100644 index 0000000..8383e83 --- /dev/null +++ b/papers/wording/fragments/object.tex @@ -0,0 +1,1673 @@ +\rSec3[expected.expected]{Class template expected} + +\rSec4[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} + +\rSec4[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} + +\rSec4[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} + +\rSec4[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} + +\rSec4[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 +\tcode{is_swappable_v} is \tcode{true} and (\tcode{is_reference_v} or \tcode{is_swappable_v}) is \tcode{true}, and \tcode{is_move_constructible_v && is_move_constructible_v} is \tcode{true}, and \tcode{is_nothrow_move_constructible_v || is_nothrow_move_constructible_v} is \tcode{true}. + +\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} + +\rSec4[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} + +\rSec4[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} + +\rSec4[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/ref.tex b/papers/wording/fragments/ref.tex new file mode 100644 index 0000000..09d1813 --- /dev/null +++ b/papers/wording/fragments/ref.tex @@ -0,0 +1,1340 @@ +\rSec3[expected.ref]{Partial specialization of expected for reference types} + +\rSec4[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) + // ------------------------------------------------------------------------- + + 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} + +\rSec4[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} + +\rSec4[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} + +\rSec4[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. + +\pnum +This operator is trivial. Copy assignment (trivial path) +\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} + +\rSec4[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} + +\rSec4[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} + +\rSec4[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} + +\rSec4[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/unexpected.tex b/papers/wording/fragments/unexpected.tex new file mode 100644 index 0000000..9957c82 --- /dev/null +++ b/papers/wording/fragments/unexpected.tex @@ -0,0 +1,356 @@ +\rSec3[expected.unexpected]{Class template unexpected} + +\rSec4[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} + +\rSec4[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} + +\rSec4[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} + +\rSec4[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} + +\rSec4[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} + +\rSec4[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/void.tex b/papers/wording/fragments/void.tex new file mode 100644 index 0000000..02d27ed --- /dev/null +++ b/papers/wording/fragments/void.tex @@ -0,0 +1,1165 @@ +\rSec3[expected.void]{Partial specialization of expected for void types} + +\rSec4[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} + +\rSec4[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} + +\rSec4[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} + +\rSec4[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} + +\rSec4[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_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} + +\rSec4[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} + +\rSec4[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} + +\rSec4[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/generate.sh b/papers/wording/generate.sh new file mode 100755 index 0000000..79bf10a --- /dev/null +++ b/papers/wording/generate.sh @@ -0,0 +1,94 @@ +#!/usr/bin/env bash +# papers/wording/generate.sh -*-sh-*- +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +# Regenerates the [expected] clause wording from the annotated headers in +# include/beman/expected/, via specgen (https://github.com/steve-downey/specgen). +# +# Produces: +# papers/wording/fragments/*.tex - one fragment per rSec2 clause, for +# \input into a paper's own document. +# papers/wording/expected.tex - all fragments concatenated in real +# standard clause order, wrapped in a +# single \rSec1[expected]{Expected +# objects}. This is the "wording only" +# file: plain generated prose, with no +# \input directives, suitable as the +# basis for a diff against the actual +# draft (github.com/cplusplus/draft) +# source/utilities.tex. +# +# specgen must be on PATH. Run from anywhere; paths below are relative to +# this script's location. +set -euo pipefail + +here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +repo_root="$(cd "$here/../.." && pwd)" +include_dir="$repo_root/include" +fragments_dir="$here/fragments" +work_dir="$(mktemp -d)" +trap 'rm -rf "$work_dir"' EXIT + +mkdir -p "$fragments_dir" + +gen() { + local header="$1" outdir="$2" + mkdir -p "$outdir" + specgen generate "$include_dir/beman/expected/$header" \ + --backend latex --validate --no-compile-commands \ + --split "$outdir" \ + -- -std=c++2c -I "$include_dir" +} + +echo "Generating from unexpected.hpp..." >&2 +gen unexpected.hpp "$work_dir/unexpected" +echo "Generating from bad_expected_access.hpp..." >&2 +gen bad_expected_access.hpp "$work_dir/bad" +echo "Generating from expected.hpp..." >&2 +gen expected.hpp "$work_dir/expected" + +# Map specgen's stable-name-derived filenames to the fragment names we keep. +cp "$work_dir/unexpected/expected.unexpected.tex" "$fragments_dir/unexpected.tex" +cp "$work_dir/bad/expected.bad.tex" "$fragments_dir/bad.tex" +cp "$work_dir/bad/expected.bad.void.tex" "$fragments_dir/bad-void.tex" +cp "$work_dir/expected/expected.expected.tex" "$fragments_dir/object.tex" +cp "$work_dir/expected/expected.void.tex" "$fragments_dir/void.tex" +cp "$work_dir/expected/expected.ref.tex" "$fragments_dir/ref.tex" + +# The generic per-header "root" fragments (named after the longest common +# stable-name prefix, e.g. expected.tex) hold exposition-only helper +# declarations (is_unexpected_specialization, reinit_expected, +# unexpect_dangles_v, converts_from_any_cvref) that live above any \rSec +# marker in the header. They are not part of the standard's own wording +# (the real draft states an equivalent helper, reinit-expected, inline in +# [expected.object.assign]'s own intro instead) and are intentionally +# omitted from the assembled clause below. + +out="$here/expected.tex" +{ + echo '% papers/wording/expected.tex -*-LaTeX-*-' + echo '% SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception' + echo '%' + echo '% Generated by papers/wording/generate.sh from the annotated headers in' + echo '% include/beman/expected/ via specgen. Do not edit by hand: re-run' + echo '% generate.sh after changing a header'\''s //! docblocks instead.' + echo '%' + echo '% specgen numbers each fragment'\''s \rSec markers one level deeper than' + echo '% written in the header ([expected.unexpected] etc. render as \rSec3, their' + echo '% subclauses as \rSec4), matching the real standard'\''s absolute numbering' + echo '% (22.8.3, 22.8.3.1, ...) directly. This file is therefore everything that' + echo '% sits *inside* the existing \rSec2[expected]{Expected objects} in the' + echo '% draft'\''s source/utilities.tex, in clause order, ready to replace the' + echo '% current [expected.unexpected] through [expected.void] subclauses and add' + echo '% the new [expected.ref] one after them -- no \rSec2[expected] wrapper and' + echo '% no \input directives. [expected.general] and [expected.syn] are prose,' + echo '% not generated from any one declaration; see papers/expected-new.tex.' + echo + for f in unexpected.tex bad.tex bad-void.tex object.tex void.tex ref.tex; do + cat "$fragments_dir/$f" + echo + done +} > "$out" + +echo "Wrote $out" >&2 +echo "Wrote $fragments_dir/{unexpected,bad,bad-void,object,void,ref}.tex" >&2 From 2261cbdc37bfe338eab6e655643e3bad722fe620 Mon Sep 17 00:00:00 2001 From: Steve Downey Date: Thu, 10 Sep 2026 00:43:00 -0400 Subject: [PATCH 04/10] fix: gather unexpected.hpp/bad_expected_access.hpp into expected.hpp's document A newer specgen enforces that a name used in one generate invocation's wording must be documented in that same run, which broke generating expected.hpp on its own: it uses unexpected, unexpect, unexpect_t, reference_constructs_from_temporary_v, and bad_expected_access, all declared in the other two headers. Wrap expected.hpp's existing #includes of unexpected.hpp and bad_expected_access.hpp in a gathered \rSec2[expected.syn] region so specgen treats all three headers as one document, and add a throwaway \rSec2[expected.detail] marker so the exposition-only helper templates above [expected.expected] don't bleed into [expected.bad]'s fragment once nothing else is there to close the section. generate.sh now runs specgen once on expected.hpp instead of three times, mapping the same six clause fragments as before plus the two new non-clause fragments, excluded exactly like the previous per-header root fragments were. unexpected.tex/bad.tex/bad-void.tex regenerate byte-identical to the prior per-header runs; object.tex/void.tex/ref.tex pick up only this specgen version's own docblock-element reordering, confirmed by equal itemdecl counts before and after. --- include/beman/expected/expected.hpp | 3 + papers/wording/expected.tex | 614 ++++++++++++++-------------- papers/wording/fragments/object.tex | 236 +++++------ papers/wording/fragments/ref.tex | 214 +++++----- papers/wording/fragments/void.tex | 146 +++---- papers/wording/generate.sh | 67 ++- 6 files changed, 638 insertions(+), 642 deletions(-) diff --git a/include/beman/expected/expected.hpp b/include/beman/expected/expected.hpp index 1190d56..82451fe 100644 --- a/include/beman/expected/expected.hpp +++ b/include/beman/expected/expected.hpp @@ -3,8 +3,10 @@ #ifndef BEMAN_EXPECTED_EXPECTED_HPP #define BEMAN_EXPECTED_EXPECTED_HPP +// \rSec2[expected.syn]{Header synopsis} #include #include +/// END [expected.syn] #ifndef BEMAN_EXPECTED_INCLUDED_FROM_INTERFACE_UNIT #include @@ -56,6 +58,7 @@ namespace expected { namespace detail { +// \rSec2[expected.detail]{Exposition-only helpers} //! \expos template struct is_expected_specialization : std::false_type {}; diff --git a/papers/wording/expected.tex b/papers/wording/expected.tex index a178a5a..37ea101 100644 --- a/papers/wording/expected.tex +++ b/papers/wording/expected.tex @@ -947,36 +947,32 @@ \indexlibraryctor{expected}% \begin{itemdecl} -constexpr expected() noexcept(is_nothrow_default_constructible_v); +constexpr expected(const expected&) = default; \end{itemdecl} \begin{itemdescr} -\pnum -\constraints -\tcode{is_default_constructible_v} is \tcode{true}. - \pnum \effects -Value-initializes \tcode{val}. +Direct-non-list-initializes \tcode{val} or \tcode{unex} (matching \tcode{rhs}'s active member) by trivial copy construction. \pnum \ensures -\tcode{has_value()} is \tcode{true}. +\tcode{rhs.has_value() == this->has_value()}. \pnum -\throws -Any exception thrown by the initialization of \tcode{val}. +\remarks +This constructor is trivial. \end{itemdescr} \indexlibraryctor{expected}% \begin{itemdecl} -constexpr expected(const expected&) = default; +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 copy construction. +Direct-non-list-initializes \tcode{val} or \tcode{unex} (matching \tcode{rhs}'s active member) by trivial move construction. \pnum \ensures @@ -989,49 +985,92 @@ \indexlibraryctor{expected}% \begin{itemdecl} -constexpr expected(const expected& rhs) noexcept(is_nothrow_copy_constructible_v && - is_nothrow_copy_constructible_v); +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(@\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 +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} +constexpr expected() noexcept(is_nothrow_default_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}. +\tcode{is_default_constructible_v} is \tcode{true}. \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()}. +Value-initializes \tcode{val}. \pnum \ensures -\tcode{rhs.has_value() == this->has_value()}. +\tcode{has_value()} is \tcode{true}. \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}. +Any exception thrown by the initialization of \tcode{val}. \end{itemdescr} \indexlibraryctor{expected}% \begin{itemdecl} -constexpr expected(expected&&) = default; +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 -Direct-non-list-initializes \tcode{val} or \tcode{unex} (matching \tcode{rhs}'s active member) by trivial move construction. +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 trivial. +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}% @@ -1229,19 +1268,6 @@ 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 @@ -1317,19 +1343,6 @@ 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 @@ -1359,19 +1372,6 @@ 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} - \rSec4[expected.object.dtor]{Destructor} \indexlibrarydtor{expected}% @@ -1442,27 +1442,24 @@ \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); +constexpr expected& operator=(expected&&) noexcept; \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}. +\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 -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. +Trivially moves \tcode{rhs}'s active member into \tcode{*this}. \pnum \returns @@ -1470,29 +1467,45 @@ \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}. +This operator is trivial. \end{itemdescr} \indexlibrarymember{operator=}{expected}% \begin{itemdecl} -constexpr expected& operator=(expected&&) noexcept; +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{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_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}. +\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 -Trivially moves \tcode{rhs}'s active member into \tcode{*this}. +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 @@ -1500,7 +1513,7 @@ \pnum \remarks -This operator is trivial. +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}% @@ -1624,19 +1637,6 @@ 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 @@ -1673,6 +1673,27 @@ \rSec4[expected.object.swap]{Swap} +\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} + \indexlibrarymember{swap}{expected}% \begin{itemdecl} constexpr void swap(expected& rhs) noexcept(is_nothrow_move_constructible_v && @@ -1700,27 +1721,6 @@ 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} - \rSec4[expected.object.obs]{Observers} \indexlibrarymember{operator->}{expected}% @@ -2511,28 +2511,67 @@ \indexlibraryctor{expected}% \begin{itemdecl} -constexpr expected() noexcept; +template + requires(is_reference_v && !is_reference_v) +constexpr expected(const unexpected&); \end{itemdecl} \begin{itemdescr} \pnum -\ensures -\tcode{has_value()} is \tcode{true}. +\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 expected(const expected& rhs) noexcept(is_nothrow_copy_constructible_v); +template + requires(@\exposidnc{unexpect-dangles-v}@) +constexpr expected(unexpect_t, Args&&...); \end{itemdecl} \begin{itemdescr} \pnum -\constraints -\tcode{is_copy_constructible_v} is \tcode{true} and \tcode{is_trivially_copy_constructible_v} is \tcode{false}. +\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} -\pnum -\effects -If \tcode{rhs.has_value()} is \tcode{false}, direct-non-list-initializes \tcode{unex} with \tcode{rhs.error()}. +\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} +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 @@ -2670,19 +2709,6 @@ 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; @@ -2719,19 +2745,6 @@ 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 @@ -2761,19 +2774,6 @@ 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 @@ -2827,6 +2827,19 @@ \rSec4[expected.void.assign]{Assignment} +\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{operator=}{expected}% \begin{itemdecl} constexpr expected& operator=(const expected& rhs) noexcept( @@ -2936,32 +2949,34 @@ This operator never throws: the referent is bound, not copied. \end{itemdescr} -\indexlibrarymember{operator=}{expected}% +\indexlibrarymember{emplace}{expected}% \begin{itemdecl} -template - requires(is_reference_v && !is_reference_v) -constexpr expected& operator=(const unexpected&); +constexpr void emplace() noexcept; \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. +\effects +If \tcode{has_value()} is \tcode{false}, destroys \tcode{unex} and sets \tcode{has_val} to \tcode{true}. \end{itemdescr} -\indexlibrarymember{emplace}{expected}% +\rSec4[expected.void.swap]{Swap} + +\indexlibraryglobal{swap}% \begin{itemdecl} -constexpr void emplace() noexcept; +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 -If \tcode{has_value()} is \tcode{false}, destroys \tcode{unex} and sets \tcode{has_val} to \tcode{true}. +Equivalent to \tcode{x.swap(y)}. \end{itemdescr} -\rSec4[expected.void.swap]{Swap} - \indexlibrarymember{swap}{expected}% \begin{itemdecl} constexpr void swap(expected& rhs) noexcept(is_nothrow_move_constructible_v && @@ -2987,23 +3002,21 @@ The exception specification is equivalent to \tcode{is_nothrow_move_constructible_v && (is_reference_v || is_nothrow_swappable_v)}. \end{itemdescr} -\indexlibraryglobal{swap}% +\rSec4[expected.void.obs]{Observers} + +\indexlibrarymember{value_or}{expected}% \begin{itemdecl} -friend constexpr void swap(expected& x, expected& y) noexcept(noexcept(x.swap(y))); +template + requires is_reference_v +constexpr void value_or(U&&) const; \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)}. +\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} -\rSec4[expected.void.obs]{Observers} - \indexlibrarymember{operator bool}{expected}% \indexlibrarymember{has_value}{expected}% \begin{itemdecl} @@ -3126,19 +3139,6 @@ \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} - \rSec4[expected.void.monadic]{Monadic operations} \indexlibrarymember{and_then}{expected}% @@ -3741,30 +3741,6 @@ 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; @@ -3833,6 +3809,69 @@ 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_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(@\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 +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} +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} template @@ -3936,19 +3975,6 @@ 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 @@ -3970,19 +3996,6 @@ \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 @@ -4008,19 +4021,6 @@ 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} - \rSec4[expected.ref.dtor]{Destructor} \indexlibrarydtor{expected}% @@ -4091,21 +4091,17 @@ \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); +constexpr expected& operator=(expected&&) noexcept; \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}. +\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. In every case \tcode{*this} comes to refer to the object \tcode{rhs} refers to, or to hold the error of \tcode{rhs}. +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 @@ -4113,53 +4109,70 @@ \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}. +This operator is trivial. \end{itemdescr} \indexlibrarymember{operator=}{expected}% \begin{itemdecl} -constexpr expected& operator=(expected&&) noexcept; +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{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}. +\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 -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. +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_reference_v) +constexpr expected& operator=(const unexpected&); +\end{itemdecl} + +\begin{itemdescr} \pnum \remarks -This operator is trivial. +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{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); +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{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}. +\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 -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. +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}% @@ -4217,19 +4230,6 @@ 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 @@ -4253,11 +4253,9 @@ \rSec4[expected.ref.swap]{Swap} -\indexlibrarymember{swap}{expected}% +\indexlibraryglobal{swap}% \begin{itemdecl} -constexpr void swap(expected& rhs) noexcept(is_nothrow_move_constructible_v && - (is_reference_v || - is_nothrow_swappable_v)); +friend constexpr void swap(expected& x, expected& y) noexcept(noexcept(x.swap(y))); \end{itemdecl} \begin{itemdescr} @@ -4267,16 +4265,14 @@ \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)}. +Equivalent to \tcode{x.swap(y)}. \end{itemdescr} -\indexlibraryglobal{swap}% +\indexlibrarymember{swap}{expected}% \begin{itemdecl} -friend constexpr void swap(expected& x, expected& y) noexcept(noexcept(x.swap(y))); +constexpr void swap(expected& rhs) noexcept(is_nothrow_move_constructible_v && + (is_reference_v || + is_nothrow_swappable_v)); \end{itemdecl} \begin{itemdescr} @@ -4286,7 +4282,11 @@ \pnum \effects -Equivalent to \tcode{x.swap(y)}. +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} \rSec4[expected.ref.obs]{Observers} diff --git a/papers/wording/fragments/object.tex b/papers/wording/fragments/object.tex index 8383e83..c396dd6 100644 --- a/papers/wording/fragments/object.tex +++ b/papers/wording/fragments/object.tex @@ -449,36 +449,32 @@ \indexlibraryctor{expected}% \begin{itemdecl} -constexpr expected() noexcept(is_nothrow_default_constructible_v); +constexpr expected(const expected&) = default; \end{itemdecl} \begin{itemdescr} -\pnum -\constraints -\tcode{is_default_constructible_v} is \tcode{true}. - \pnum \effects -Value-initializes \tcode{val}. +Direct-non-list-initializes \tcode{val} or \tcode{unex} (matching \tcode{rhs}'s active member) by trivial copy construction. \pnum \ensures -\tcode{has_value()} is \tcode{true}. +\tcode{rhs.has_value() == this->has_value()}. \pnum -\throws -Any exception thrown by the initialization of \tcode{val}. +\remarks +This constructor is trivial. \end{itemdescr} \indexlibraryctor{expected}% \begin{itemdecl} -constexpr expected(const expected&) = default; +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 copy construction. +Direct-non-list-initializes \tcode{val} or \tcode{unex} (matching \tcode{rhs}'s active member) by trivial move construction. \pnum \ensures @@ -491,49 +487,92 @@ \indexlibraryctor{expected}% \begin{itemdecl} -constexpr expected(const expected& rhs) noexcept(is_nothrow_copy_constructible_v && - is_nothrow_copy_constructible_v); +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(@\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 +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} +constexpr expected() noexcept(is_nothrow_default_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}. +\tcode{is_default_constructible_v} is \tcode{true}. \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()}. +Value-initializes \tcode{val}. \pnum \ensures -\tcode{rhs.has_value() == this->has_value()}. +\tcode{has_value()} is \tcode{true}. \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}. +Any exception thrown by the initialization of \tcode{val}. \end{itemdescr} \indexlibraryctor{expected}% \begin{itemdecl} -constexpr expected(expected&&) = default; +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 -Direct-non-list-initializes \tcode{val} or \tcode{unex} (matching \tcode{rhs}'s active member) by trivial move construction. +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 trivial. +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}% @@ -731,19 +770,6 @@ 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 @@ -819,19 +845,6 @@ 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 @@ -861,19 +874,6 @@ 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} - \rSec4[expected.object.dtor]{Destructor} \indexlibrarydtor{expected}% @@ -944,27 +944,24 @@ \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); +constexpr expected& operator=(expected&&) noexcept; \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}. +\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 -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. +Trivially moves \tcode{rhs}'s active member into \tcode{*this}. \pnum \returns @@ -972,29 +969,45 @@ \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}. +This operator is trivial. \end{itemdescr} \indexlibrarymember{operator=}{expected}% \begin{itemdecl} -constexpr expected& operator=(expected&&) noexcept; +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{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_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}. +\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 -Trivially moves \tcode{rhs}'s active member into \tcode{*this}. +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 @@ -1002,7 +1015,7 @@ \pnum \remarks -This operator is trivial. +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}% @@ -1126,19 +1139,6 @@ 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 @@ -1175,6 +1175,27 @@ \rSec4[expected.object.swap]{Swap} +\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} + \indexlibrarymember{swap}{expected}% \begin{itemdecl} constexpr void swap(expected& rhs) noexcept(is_nothrow_move_constructible_v && @@ -1202,27 +1223,6 @@ 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} - \rSec4[expected.object.obs]{Observers} \indexlibrarymember{operator->}{expected}% diff --git a/papers/wording/fragments/ref.tex b/papers/wording/fragments/ref.tex index 09d1813..e5a02dc 100644 --- a/papers/wording/fragments/ref.tex +++ b/papers/wording/fragments/ref.tex @@ -403,30 +403,6 @@ 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; @@ -495,6 +471,69 @@ 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_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(@\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 +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} +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} template @@ -598,19 +637,6 @@ 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 @@ -632,19 +658,6 @@ \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 @@ -670,19 +683,6 @@ 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} - \rSec4[expected.ref.dtor]{Destructor} \indexlibrarydtor{expected}% @@ -753,21 +753,17 @@ \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); +constexpr expected& operator=(expected&&) noexcept; \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}. +\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. In every case \tcode{*this} comes to refer to the object \tcode{rhs} refers to, or to hold the error of \tcode{rhs}. +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 @@ -775,53 +771,70 @@ \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}. +This operator is trivial. \end{itemdescr} \indexlibrarymember{operator=}{expected}% \begin{itemdecl} -constexpr expected& operator=(expected&&) noexcept; +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{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}. +\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 -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. +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_reference_v) +constexpr expected& operator=(const unexpected&); +\end{itemdecl} + +\begin{itemdescr} \pnum \remarks -This operator is trivial. +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{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); +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{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}. +\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 -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. +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}% @@ -879,19 +892,6 @@ 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 @@ -915,11 +915,9 @@ \rSec4[expected.ref.swap]{Swap} -\indexlibrarymember{swap}{expected}% +\indexlibraryglobal{swap}% \begin{itemdecl} -constexpr void swap(expected& rhs) noexcept(is_nothrow_move_constructible_v && - (is_reference_v || - is_nothrow_swappable_v)); +friend constexpr void swap(expected& x, expected& y) noexcept(noexcept(x.swap(y))); \end{itemdecl} \begin{itemdescr} @@ -929,16 +927,14 @@ \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)}. +Equivalent to \tcode{x.swap(y)}. \end{itemdescr} -\indexlibraryglobal{swap}% +\indexlibrarymember{swap}{expected}% \begin{itemdecl} -friend constexpr void swap(expected& x, expected& y) noexcept(noexcept(x.swap(y))); +constexpr void swap(expected& rhs) noexcept(is_nothrow_move_constructible_v && + (is_reference_v || + is_nothrow_swappable_v)); \end{itemdecl} \begin{itemdescr} @@ -948,7 +944,11 @@ \pnum \effects -Equivalent to \tcode{x.swap(y)}. +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} \rSec4[expected.ref.obs]{Observers} diff --git a/papers/wording/fragments/void.tex b/papers/wording/fragments/void.tex index 02d27ed..65798ca 100644 --- a/papers/wording/fragments/void.tex +++ b/papers/wording/fragments/void.tex @@ -337,6 +337,45 @@ \rSec4[expected.void.cons]{Constructors} +\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(@\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 +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} constexpr expected() noexcept; @@ -498,19 +537,6 @@ 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; @@ -547,19 +573,6 @@ 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 @@ -589,19 +602,6 @@ 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 @@ -655,6 +655,19 @@ \rSec4[expected.void.assign]{Assignment} +\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{operator=}{expected}% \begin{itemdecl} constexpr expected& operator=(const expected& rhs) noexcept( @@ -764,32 +777,34 @@ This operator never throws: the referent is bound, not copied. \end{itemdescr} -\indexlibrarymember{operator=}{expected}% +\indexlibrarymember{emplace}{expected}% \begin{itemdecl} -template - requires(is_reference_v && !is_reference_v) -constexpr expected& operator=(const unexpected&); +constexpr void emplace() noexcept; \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. +\effects +If \tcode{has_value()} is \tcode{false}, destroys \tcode{unex} and sets \tcode{has_val} to \tcode{true}. \end{itemdescr} -\indexlibrarymember{emplace}{expected}% +\rSec4[expected.void.swap]{Swap} + +\indexlibraryglobal{swap}% \begin{itemdecl} -constexpr void emplace() noexcept; +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 -If \tcode{has_value()} is \tcode{false}, destroys \tcode{unex} and sets \tcode{has_val} to \tcode{true}. +Equivalent to \tcode{x.swap(y)}. \end{itemdescr} -\rSec4[expected.void.swap]{Swap} - \indexlibrarymember{swap}{expected}% \begin{itemdecl} constexpr void swap(expected& rhs) noexcept(is_nothrow_move_constructible_v && @@ -815,23 +830,21 @@ The exception specification is equivalent to \tcode{is_nothrow_move_constructible_v && (is_reference_v || is_nothrow_swappable_v)}. \end{itemdescr} -\indexlibraryglobal{swap}% +\rSec4[expected.void.obs]{Observers} + +\indexlibrarymember{value_or}{expected}% \begin{itemdecl} -friend constexpr void swap(expected& x, expected& y) noexcept(noexcept(x.swap(y))); +template + requires is_reference_v +constexpr void value_or(U&&) const; \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)}. +\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} -\rSec4[expected.void.obs]{Observers} - \indexlibrarymember{operator bool}{expected}% \indexlibrarymember{has_value}{expected}% \begin{itemdecl} @@ -954,19 +967,6 @@ \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} - \rSec4[expected.void.monadic]{Monadic operations} \indexlibrarymember{and_then}{expected}% diff --git a/papers/wording/generate.sh b/papers/wording/generate.sh index 79bf10a..0f0bf83 100755 --- a/papers/wording/generate.sh +++ b/papers/wording/generate.sh @@ -4,18 +4,21 @@ # # Regenerates the [expected] clause wording from the annotated headers in # include/beman/expected/, via specgen (https://github.com/steve-downey/specgen). +# expected.hpp gathers unexpected.hpp and bad_expected_access.hpp into one +# document (their #includes sit inside a \rSec2[expected.syn] ... END [expected.syn] +# region), so a single specgen invocation on expected.hpp covers all three +# headers' wording in one run. # # Produces: -# papers/wording/fragments/*.tex - one fragment per rSec2 clause, for +# papers/wording/fragments/*.tex - one fragment per top-level clause, for # \input into a paper's own document. # papers/wording/expected.tex - all fragments concatenated in real -# standard clause order, wrapped in a -# single \rSec1[expected]{Expected -# objects}. This is the "wording only" -# file: plain generated prose, with no -# \input directives, suitable as the -# basis for a diff against the actual -# draft (github.com/cplusplus/draft) +# standard clause order. This is the +# "wording only" file: plain generated +# prose, with no \input directives, +# suitable as the basis for a diff +# against the actual draft +# (github.com/cplusplus/draft) # source/utilities.tex. # # specgen must be on PATH. Run from anywhere; paths below are relative to @@ -31,38 +34,28 @@ trap 'rm -rf "$work_dir"' EXIT mkdir -p "$fragments_dir" -gen() { - local header="$1" outdir="$2" - mkdir -p "$outdir" - specgen generate "$include_dir/beman/expected/$header" \ - --backend latex --validate --no-compile-commands \ - --split "$outdir" \ - -- -std=c++2c -I "$include_dir" -} - -echo "Generating from unexpected.hpp..." >&2 -gen unexpected.hpp "$work_dir/unexpected" -echo "Generating from bad_expected_access.hpp..." >&2 -gen bad_expected_access.hpp "$work_dir/bad" -echo "Generating from expected.hpp..." >&2 -gen expected.hpp "$work_dir/expected" +echo "Generating from expected.hpp (gathers unexpected.hpp, bad_expected_access.hpp)..." >&2 +specgen generate "$include_dir/beman/expected/expected.hpp" \ + --backend latex --validate --no-compile-commands \ + --split "$work_dir" \ + -- -std=c++2c -I "$include_dir" # Map specgen's stable-name-derived filenames to the fragment names we keep. -cp "$work_dir/unexpected/expected.unexpected.tex" "$fragments_dir/unexpected.tex" -cp "$work_dir/bad/expected.bad.tex" "$fragments_dir/bad.tex" -cp "$work_dir/bad/expected.bad.void.tex" "$fragments_dir/bad-void.tex" -cp "$work_dir/expected/expected.expected.tex" "$fragments_dir/object.tex" -cp "$work_dir/expected/expected.void.tex" "$fragments_dir/void.tex" -cp "$work_dir/expected/expected.ref.tex" "$fragments_dir/ref.tex" +cp "$work_dir/expected.unexpected.tex" "$fragments_dir/unexpected.tex" +cp "$work_dir/expected.bad.tex" "$fragments_dir/bad.tex" +cp "$work_dir/expected.bad.void.tex" "$fragments_dir/bad-void.tex" +cp "$work_dir/expected.expected.tex" "$fragments_dir/object.tex" +cp "$work_dir/expected.void.tex" "$fragments_dir/void.tex" +cp "$work_dir/expected.ref.tex" "$fragments_dir/ref.tex" -# The generic per-header "root" fragments (named after the longest common -# stable-name prefix, e.g. expected.tex) hold exposition-only helper -# declarations (is_unexpected_specialization, reinit_expected, -# unexpect_dangles_v, converts_from_any_cvref) that live above any \rSec -# marker in the header. They are not part of the standard's own wording -# (the real draft states an equivalent helper, reinit-expected, inline in -# [expected.object.assign]'s own intro instead) and are intentionally -# omitted from the assembled clause below. +# expected.syn.tex (the header synopsis, gathered from the two #includes) and +# expected.detail.tex (a throwaway \rSec2 fencing off the exposition-only +# helpers -- is_expected_specialization, reinit_expected, unexpect_dangles_v, +# converts_from_any_cvref -- declared above [expected.expected] so they don't +# bleed into [expected.bad]) are not part of the standard's own wording (the +# real draft states an equivalent helper, reinit-expected, inline in +# [expected.object.assign]'s own intro instead) and are intentionally omitted +# from the assembled clause below. out="$here/expected.tex" { From b7ec3e1eb104f7da5766fba00eb0fa8eff0b1b25 Mon Sep 17 00:00:00 2001 From: Steve Downey Date: Thu, 10 Sep 2026 07:14:46 -0400 Subject: [PATCH 05/10] fix: don't leave a trailing blank line in generate.sh's assembled expected.tex The per-fragment loop echoed a separator after every fragment, including the last, leaving a second trailing newline that pre-commit's end-of-file-fixer then had to strip on every regeneration. Insert the separator between fragments instead. --- papers/wording/expected.tex | 1 - papers/wording/generate.sh | 4 +++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/papers/wording/expected.tex b/papers/wording/expected.tex index 37ea101..96fe0dc 100644 --- a/papers/wording/expected.tex +++ b/papers/wording/expected.tex @@ -4676,4 +4676,3 @@ \returns \tcode{!x.has_value() && static_cast(x.error() == e.error())}. \end{itemdescr} - diff --git a/papers/wording/generate.sh b/papers/wording/generate.sh index 0f0bf83..4ae9f90 100755 --- a/papers/wording/generate.sh +++ b/papers/wording/generate.sh @@ -77,9 +77,11 @@ out="$here/expected.tex" echo '% no \input directives. [expected.general] and [expected.syn] are prose,' echo '% not generated from any one declaration; see papers/expected-new.tex.' echo + sep="" for f in unexpected.tex bad.tex bad-void.tex object.tex void.tex ref.tex; do + printf '%s' "$sep" cat "$fragments_dir/$f" - echo + sep=$'\n' done } > "$out" From 2b737a44646038d39a59d0a148d761df36b80707 Mon Sep 17 00:00:00 2001 From: Steve Downey Date: Mon, 14 Sep 2026 20:29:24 -0400 Subject: [PATCH 06/10] fix: emit the wording at the draft's own \rSec levels generate.sh relied on specgen's hard-coded latex base depth of 3, and the banner and README rationalized the result as "one level deeper than written, matching the real standard's absolute numbering". It does not match: in source/utilities.tex [expected] is \rSec1 and [expected.unexpected] is \rSec2, so emitting [expected.unexpected] as \rSec3 and its subclauses as \rSec4 numbered them one level too deep for both consumers -- a patch against the draft, and an \input into a paper whose own \rSec1[expected] makes its sibling clauses \rSec2. specgen now reaches that depth from the command line (specgen#97), so pass --base-section-depth 2 and drop the compensation from the prose. Every \rSec marker a clause writes in a header now means what it says. Regenerating changes nothing but the heading levels: the stable names and their nesting are unchanged, and every clause the draft already has now sits at exactly the draft's own level. The other two specgen workarounds stay, both still load-bearing: - the gathered \rSec2[expected.syn] region over expected.hpp's #includes. specgen#109 (validate across the union of a paper's documents) removes the unexpected/bad_expected_access findings, but a split run still reports unexpect and unexpect_t (\omit'd here, since they belong to the hand-authored [expected.syn]) and reference_constructs_from_temporary_v (a using-declaration, which contributes no documented name) as foreign. - the throwaway \rSec2[expected.detail] marker. Without it the five exposition-only helpers above [expected.expected] still bleed into [expected.bad]'s fragment. Co-Authored-By: Claude Opus 5 (1M context) --- papers/wording/README.md | 17 ++--- papers/wording/expected.tex | 92 ++++++++++++------------- papers/wording/fragments/bad-void.tex | 2 +- papers/wording/fragments/bad.tex | 2 +- papers/wording/fragments/object.tex | 18 ++--- papers/wording/fragments/ref.tex | 18 ++--- papers/wording/fragments/unexpected.tex | 14 ++-- papers/wording/fragments/void.tex | 18 ++--- papers/wording/generate.sh | 24 ++++--- 9 files changed, 104 insertions(+), 101 deletions(-) diff --git a/papers/wording/README.md b/papers/wording/README.md index 6b39c2f..1af7238 100644 --- a/papers/wording/README.md +++ b/papers/wording/README.md @@ -16,12 +16,13 @@ hand-edit the files here; the header comments are the source of truth. All the generated subclauses concatenated in real standard order — `[expected.unexpected]` (including the new `[expected.un.ref]`) through -`[expected.ref.eq]`. `specgen` numbers a fragment's `\rSec` markers one level -deeper than written in the header, which lines up with the draft's own -absolute numbering (`22.8.3`, `22.8.3.1`, ...). This file is everything that -sits *inside* the existing `\rSec2[expected]{Expected objects}` in +`[expected.ref.eq]`. `generate.sh` 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 `\rSec2[expected]`, no `\input` directives — so it's the basis for a +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. @@ -34,6 +35,6 @@ hand-authored versions of both. The same content, split one file per top-level clause (`unexpected.tex`, `bad.tex`, `bad-void.tex`, `object.tex`, `void.tex`, -`ref.tex`) — for `\input` into a standalone paper's own document, where the -enclosing `\rSec1[expected]{Expected objects}` (see `papers/expected-new.tex`) -supplies the level that `expected.tex` above assumes already exists. +`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. diff --git a/papers/wording/expected.tex b/papers/wording/expected.tex index 96fe0dc..bf02314 100644 --- a/papers/wording/expected.tex +++ b/papers/wording/expected.tex @@ -5,20 +5,20 @@ % include/beman/expected/ via specgen. Do not edit by hand: re-run % generate.sh after changing a header's //! docblocks instead. % -% specgen numbers each fragment's \rSec markers one level deeper than -% written in the header ([expected.unexpected] etc. render as \rSec3, their -% subclauses as \rSec4), matching the real standard's absolute numbering -% (22.8.3, 22.8.3.1, ...) directly. This file is therefore everything that -% sits *inside* the existing \rSec2[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 \rSec2[expected] wrapper and -% no \input directives. [expected.general] and [expected.syn] are prose, -% not generated from any one declaration; see papers/expected-new.tex. +% --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. -\rSec3[expected.unexpected]{Class template unexpected} +\rSec2[expected.unexpected]{Class template unexpected} -\rSec4[expected.un.general]{General} +\rSec3[expected.un.general]{General} \begin{codeblock} template @@ -77,7 +77,7 @@ Subclause \iref{expected.unexpected} describes the class template \tcode{unexpected} that represents unexpected objects stored in \tcode{expected} objects. \end{itemdescr} -\rSec4[expected.un.cons]{Constructors} +\rSec3[expected.un.cons]{Constructors} \indexlibraryctor{unexpected}% \begin{itemdecl} @@ -151,7 +151,7 @@ Any exception thrown by the initialization of \tcode{unex}. \end{itemdescr} -\rSec4[expected.un.obs]{Observers} +\rSec3[expected.un.obs]{Observers} \indexlibrarymember{error}{unexpected}% \begin{itemdecl} @@ -185,7 +185,7 @@ constexpr E&& error() && noexcept; \end{itemdecl} -\rSec4[expected.un.swap]{Swap} +\rSec3[expected.un.swap]{Swap} \indexlibrarymember{swap}{unexpected}% \begin{itemdecl} @@ -217,7 +217,7 @@ Equivalent to \tcode{x.swap(y)}. \end{itemdescr} -\rSec4[expected.un.eq]{Equality operator} +\rSec3[expected.un.eq]{Equality operator} \indexlibraryglobal{operator==}% \begin{itemdecl} @@ -235,7 +235,7 @@ \tcode{x.error() == y.error()}. \end{itemdescr} -\rSec4[expected.un.ref]{Partial specialization unexpected} +\rSec3[expected.un.ref]{Partial specialization unexpected} \begin{codeblock} template @@ -373,7 +373,7 @@ Equivalent to \tcode{x.swap(y)}. \end{itemdescr} -\rSec3[expected.bad]{Class template bad_expected_access} +\rSec2[expected.bad]{Class template bad_expected_access} \indexlibrarymember{what}{bad_expected_access}% \begin{itemdecl} @@ -474,7 +474,7 @@ \tcode{std::move(unex)}. \end{itemdescr} -\rSec3[expected.bad.void]{Class template specialization bad_expected_access} +\rSec2[expected.bad.void]{Class template specialization bad_expected_access} \begin{codeblock} template<> @@ -496,9 +496,9 @@ }; \end{codeblock} -\rSec3[expected.expected]{Class template expected} +\rSec2[expected.expected]{Class template expected} -\rSec4[expected.object.general]{General} +\rSec3[expected.object.general]{General} \begin{codeblock} template @@ -943,7 +943,7 @@ 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} -\rSec4[expected.object.cons]{Constructors} +\rSec3[expected.object.cons]{Constructors} \indexlibraryctor{expected}% \begin{itemdecl} @@ -1372,7 +1372,7 @@ 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} -\rSec4[expected.object.dtor]{Destructor} +\rSec3[expected.object.dtor]{Destructor} \indexlibrarydtor{expected}% \begin{itemdecl} @@ -1408,7 +1408,7 @@ If \tcode{has_value()} is \tcode{true}, destroys \tcode{val}, otherwise destroys \tcode{unex}. \end{itemdescr} -\rSec4[expected.object.assign]{Assignment} +\rSec3[expected.object.assign]{Assignment} \indexlibrarymember{operator=}{expected}% \begin{itemdecl} @@ -1671,7 +1671,7 @@ 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} -\rSec4[expected.object.swap]{Swap} +\rSec3[expected.object.swap]{Swap} \indexlibraryglobal{swap}% \begin{itemdecl} @@ -1721,7 +1721,7 @@ 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} -\rSec4[expected.object.obs]{Observers} +\rSec3[expected.object.obs]{Observers} \indexlibrarymember{operator->}{expected}% \begin{itemdecl} @@ -1922,7 +1922,7 @@ \tcode{std::forward(def)} if \tcode{has_value()} is \tcode{true}, \tcode{std::move(error())} otherwise. \end{itemdescr} -\rSec4[expected.object.monadic]{Monadic operations} +\rSec3[expected.object.monadic]{Monadic operations} \indexlibrarymember{and_then}{expected}% \begin{itemdecl} @@ -2118,7 +2118,7 @@ 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} -\rSec4[expected.object.eq]{Equality operators} +\rSec3[expected.object.eq]{Equality operators} \indexlibraryglobal{operator==}% \begin{itemdecl} @@ -2170,9 +2170,9 @@ \tcode{!x.has_value() && static_cast(x.error() == e.error())}. \end{itemdescr} -\rSec3[expected.void]{Partial specialization of expected for void types} +\rSec2[expected.void]{Partial specialization of expected for void types} -\rSec4[expected.void.general]{General} +\rSec3[expected.void.general]{General} \begin{codeblock} template @@ -2507,7 +2507,7 @@ 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} -\rSec4[expected.void.cons]{Constructors} +\rSec3[expected.void.cons]{Constructors} \indexlibraryctor{expected}% \begin{itemdecl} @@ -2804,7 +2804,7 @@ 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} -\rSec4[expected.void.dtor]{Destructor} +\rSec3[expected.void.dtor]{Destructor} \indexlibrarydtor{expected}% \begin{itemdecl} @@ -2825,7 +2825,7 @@ If \tcode{is_trivially_destructible_v} is \tcode{true}, then this destructor is a trivial destructor. \end{itemdescr} -\rSec4[expected.void.assign]{Assignment} +\rSec3[expected.void.assign]{Assignment} \indexlibrarymember{operator=}{expected}% \begin{itemdecl} @@ -2960,7 +2960,7 @@ If \tcode{has_value()} is \tcode{false}, destroys \tcode{unex} and sets \tcode{has_val} to \tcode{true}. \end{itemdescr} -\rSec4[expected.void.swap]{Swap} +\rSec3[expected.void.swap]{Swap} \indexlibraryglobal{swap}% \begin{itemdecl} @@ -3002,7 +3002,7 @@ The exception specification is equivalent to \tcode{is_nothrow_move_constructible_v && (is_reference_v || is_nothrow_swappable_v)}. \end{itemdescr} -\rSec4[expected.void.obs]{Observers} +\rSec3[expected.void.obs]{Observers} \indexlibrarymember{value_or}{expected}% \begin{itemdecl} @@ -3139,7 +3139,7 @@ \tcode{std::forward(def)} if \tcode{has_value()} is \tcode{true}, \tcode{std::move(error())} otherwise. \end{itemdescr} -\rSec4[expected.void.monadic]{Monadic operations} +\rSec3[expected.void.monadic]{Monadic operations} \indexlibrarymember{and_then}{expected}% \begin{itemdecl} @@ -3301,7 +3301,7 @@ 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} -\rSec4[expected.void.eq]{Equality operators} +\rSec3[expected.void.eq]{Equality operators} \indexlibraryglobal{operator==}% \begin{itemdecl} @@ -3336,9 +3336,9 @@ \tcode{!x.has_value() && static_cast(x.error() == e.error())}. \end{itemdescr} -\rSec3[expected.ref]{Partial specialization of expected for reference types} +\rSec2[expected.ref]{Partial specialization of expected for reference types} -\rSec4[expected.ref.general]{General} +\rSec3[expected.ref.general]{General} \begin{codeblock} template @@ -3709,7 +3709,7 @@ 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} -\rSec4[expected.ref.cons]{Constructors} +\rSec3[expected.ref.cons]{Constructors} \indexlibraryctor{expected}% \begin{itemdecl} @@ -4021,7 +4021,7 @@ 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} -\rSec4[expected.ref.dtor]{Destructor} +\rSec3[expected.ref.dtor]{Destructor} \indexlibrarydtor{expected}% \begin{itemdecl} @@ -4061,7 +4061,7 @@ This destructor is trivial if \tcode{E} is trivially destructible. \end{itemdescr} -\rSec4[expected.ref.assign]{Assignment} +\rSec3[expected.ref.assign]{Assignment} \indexlibrarymember{operator=}{expected}% \begin{itemdecl} @@ -4251,7 +4251,7 @@ \tcode{*val}. \end{itemdescr} -\rSec4[expected.ref.swap]{Swap} +\rSec3[expected.ref.swap]{Swap} \indexlibraryglobal{swap}% \begin{itemdecl} @@ -4289,7 +4289,7 @@ The exception specification is equivalent to \tcode{is_nothrow_move_constructible_v && (is_reference_v || is_nothrow_swappable_v)}. \end{itemdescr} -\rSec4[expected.ref.obs]{Observers} +\rSec3[expected.ref.obs]{Observers} \indexlibrarymember{operator->}{expected}% \begin{itemdecl} @@ -4453,7 +4453,7 @@ \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} -\rSec4[expected.ref.monadic]{Monadic operations} +\rSec3[expected.ref.monadic]{Monadic operations} \indexlibrarymember{and_then}{expected}% \begin{itemdecl} @@ -4621,7 +4621,7 @@ 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} -\rSec4[expected.ref.eq]{Equality operators} +\rSec3[expected.ref.eq]{Equality operators} \indexlibraryglobal{operator==}% \begin{itemdecl} diff --git a/papers/wording/fragments/bad-void.tex b/papers/wording/fragments/bad-void.tex index e630496..55b9c7d 100644 --- a/papers/wording/fragments/bad-void.tex +++ b/papers/wording/fragments/bad-void.tex @@ -1,4 +1,4 @@ -\rSec3[expected.bad.void]{Class template specialization bad_expected_access} +\rSec2[expected.bad.void]{Class template specialization bad_expected_access} \begin{codeblock} template<> diff --git a/papers/wording/fragments/bad.tex b/papers/wording/fragments/bad.tex index c084bd4..5c47ae2 100644 --- a/papers/wording/fragments/bad.tex +++ b/papers/wording/fragments/bad.tex @@ -1,4 +1,4 @@ -\rSec3[expected.bad]{Class template bad_expected_access} +\rSec2[expected.bad]{Class template bad_expected_access} \indexlibrarymember{what}{bad_expected_access}% \begin{itemdecl} diff --git a/papers/wording/fragments/object.tex b/papers/wording/fragments/object.tex index c396dd6..5b5719b 100644 --- a/papers/wording/fragments/object.tex +++ b/papers/wording/fragments/object.tex @@ -1,6 +1,6 @@ -\rSec3[expected.expected]{Class template expected} +\rSec2[expected.expected]{Class template expected} -\rSec4[expected.object.general]{General} +\rSec3[expected.object.general]{General} \begin{codeblock} template @@ -445,7 +445,7 @@ 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} -\rSec4[expected.object.cons]{Constructors} +\rSec3[expected.object.cons]{Constructors} \indexlibraryctor{expected}% \begin{itemdecl} @@ -874,7 +874,7 @@ 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} -\rSec4[expected.object.dtor]{Destructor} +\rSec3[expected.object.dtor]{Destructor} \indexlibrarydtor{expected}% \begin{itemdecl} @@ -910,7 +910,7 @@ If \tcode{has_value()} is \tcode{true}, destroys \tcode{val}, otherwise destroys \tcode{unex}. \end{itemdescr} -\rSec4[expected.object.assign]{Assignment} +\rSec3[expected.object.assign]{Assignment} \indexlibrarymember{operator=}{expected}% \begin{itemdecl} @@ -1173,7 +1173,7 @@ 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} -\rSec4[expected.object.swap]{Swap} +\rSec3[expected.object.swap]{Swap} \indexlibraryglobal{swap}% \begin{itemdecl} @@ -1223,7 +1223,7 @@ 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} -\rSec4[expected.object.obs]{Observers} +\rSec3[expected.object.obs]{Observers} \indexlibrarymember{operator->}{expected}% \begin{itemdecl} @@ -1424,7 +1424,7 @@ \tcode{std::forward(def)} if \tcode{has_value()} is \tcode{true}, \tcode{std::move(error())} otherwise. \end{itemdescr} -\rSec4[expected.object.monadic]{Monadic operations} +\rSec3[expected.object.monadic]{Monadic operations} \indexlibrarymember{and_then}{expected}% \begin{itemdecl} @@ -1620,7 +1620,7 @@ 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} -\rSec4[expected.object.eq]{Equality operators} +\rSec3[expected.object.eq]{Equality operators} \indexlibraryglobal{operator==}% \begin{itemdecl} diff --git a/papers/wording/fragments/ref.tex b/papers/wording/fragments/ref.tex index e5a02dc..6f6da96 100644 --- a/papers/wording/fragments/ref.tex +++ b/papers/wording/fragments/ref.tex @@ -1,6 +1,6 @@ -\rSec3[expected.ref]{Partial specialization of expected for reference types} +\rSec2[expected.ref]{Partial specialization of expected for reference types} -\rSec4[expected.ref.general]{General} +\rSec3[expected.ref.general]{General} \begin{codeblock} template @@ -371,7 +371,7 @@ 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} -\rSec4[expected.ref.cons]{Constructors} +\rSec3[expected.ref.cons]{Constructors} \indexlibraryctor{expected}% \begin{itemdecl} @@ -683,7 +683,7 @@ 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} -\rSec4[expected.ref.dtor]{Destructor} +\rSec3[expected.ref.dtor]{Destructor} \indexlibrarydtor{expected}% \begin{itemdecl} @@ -723,7 +723,7 @@ This destructor is trivial if \tcode{E} is trivially destructible. \end{itemdescr} -\rSec4[expected.ref.assign]{Assignment} +\rSec3[expected.ref.assign]{Assignment} \indexlibrarymember{operator=}{expected}% \begin{itemdecl} @@ -913,7 +913,7 @@ \tcode{*val}. \end{itemdescr} -\rSec4[expected.ref.swap]{Swap} +\rSec3[expected.ref.swap]{Swap} \indexlibraryglobal{swap}% \begin{itemdecl} @@ -951,7 +951,7 @@ The exception specification is equivalent to \tcode{is_nothrow_move_constructible_v && (is_reference_v || is_nothrow_swappable_v)}. \end{itemdescr} -\rSec4[expected.ref.obs]{Observers} +\rSec3[expected.ref.obs]{Observers} \indexlibrarymember{operator->}{expected}% \begin{itemdecl} @@ -1115,7 +1115,7 @@ \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} -\rSec4[expected.ref.monadic]{Monadic operations} +\rSec3[expected.ref.monadic]{Monadic operations} \indexlibrarymember{and_then}{expected}% \begin{itemdecl} @@ -1283,7 +1283,7 @@ 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} -\rSec4[expected.ref.eq]{Equality operators} +\rSec3[expected.ref.eq]{Equality operators} \indexlibraryglobal{operator==}% \begin{itemdecl} diff --git a/papers/wording/fragments/unexpected.tex b/papers/wording/fragments/unexpected.tex index 9957c82..40ce691 100644 --- a/papers/wording/fragments/unexpected.tex +++ b/papers/wording/fragments/unexpected.tex @@ -1,6 +1,6 @@ -\rSec3[expected.unexpected]{Class template unexpected} +\rSec2[expected.unexpected]{Class template unexpected} -\rSec4[expected.un.general]{General} +\rSec3[expected.un.general]{General} \begin{codeblock} template @@ -59,7 +59,7 @@ Subclause \iref{expected.unexpected} describes the class template \tcode{unexpected} that represents unexpected objects stored in \tcode{expected} objects. \end{itemdescr} -\rSec4[expected.un.cons]{Constructors} +\rSec3[expected.un.cons]{Constructors} \indexlibraryctor{unexpected}% \begin{itemdecl} @@ -133,7 +133,7 @@ Any exception thrown by the initialization of \tcode{unex}. \end{itemdescr} -\rSec4[expected.un.obs]{Observers} +\rSec3[expected.un.obs]{Observers} \indexlibrarymember{error}{unexpected}% \begin{itemdecl} @@ -167,7 +167,7 @@ constexpr E&& error() && noexcept; \end{itemdecl} -\rSec4[expected.un.swap]{Swap} +\rSec3[expected.un.swap]{Swap} \indexlibrarymember{swap}{unexpected}% \begin{itemdecl} @@ -199,7 +199,7 @@ Equivalent to \tcode{x.swap(y)}. \end{itemdescr} -\rSec4[expected.un.eq]{Equality operator} +\rSec3[expected.un.eq]{Equality operator} \indexlibraryglobal{operator==}% \begin{itemdecl} @@ -217,7 +217,7 @@ \tcode{x.error() == y.error()}. \end{itemdescr} -\rSec4[expected.un.ref]{Partial specialization unexpected} +\rSec3[expected.un.ref]{Partial specialization unexpected} \begin{codeblock} template diff --git a/papers/wording/fragments/void.tex b/papers/wording/fragments/void.tex index 65798ca..e5bc3ec 100644 --- a/papers/wording/fragments/void.tex +++ b/papers/wording/fragments/void.tex @@ -1,6 +1,6 @@ -\rSec3[expected.void]{Partial specialization of expected for void types} +\rSec2[expected.void]{Partial specialization of expected for void types} -\rSec4[expected.void.general]{General} +\rSec3[expected.void.general]{General} \begin{codeblock} template @@ -335,7 +335,7 @@ 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} -\rSec4[expected.void.cons]{Constructors} +\rSec3[expected.void.cons]{Constructors} \indexlibraryctor{expected}% \begin{itemdecl} @@ -632,7 +632,7 @@ 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} -\rSec4[expected.void.dtor]{Destructor} +\rSec3[expected.void.dtor]{Destructor} \indexlibrarydtor{expected}% \begin{itemdecl} @@ -653,7 +653,7 @@ If \tcode{is_trivially_destructible_v} is \tcode{true}, then this destructor is a trivial destructor. \end{itemdescr} -\rSec4[expected.void.assign]{Assignment} +\rSec3[expected.void.assign]{Assignment} \indexlibrarymember{operator=}{expected}% \begin{itemdecl} @@ -788,7 +788,7 @@ If \tcode{has_value()} is \tcode{false}, destroys \tcode{unex} and sets \tcode{has_val} to \tcode{true}. \end{itemdescr} -\rSec4[expected.void.swap]{Swap} +\rSec3[expected.void.swap]{Swap} \indexlibraryglobal{swap}% \begin{itemdecl} @@ -830,7 +830,7 @@ The exception specification is equivalent to \tcode{is_nothrow_move_constructible_v && (is_reference_v || is_nothrow_swappable_v)}. \end{itemdescr} -\rSec4[expected.void.obs]{Observers} +\rSec3[expected.void.obs]{Observers} \indexlibrarymember{value_or}{expected}% \begin{itemdecl} @@ -967,7 +967,7 @@ \tcode{std::forward(def)} if \tcode{has_value()} is \tcode{true}, \tcode{std::move(error())} otherwise. \end{itemdescr} -\rSec4[expected.void.monadic]{Monadic operations} +\rSec3[expected.void.monadic]{Monadic operations} \indexlibrarymember{and_then}{expected}% \begin{itemdecl} @@ -1129,7 +1129,7 @@ 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} -\rSec4[expected.void.eq]{Equality operators} +\rSec3[expected.void.eq]{Equality operators} \indexlibraryglobal{operator==}% \begin{itemdecl} diff --git a/papers/wording/generate.sh b/papers/wording/generate.sh index 4ae9f90..09bb9cf 100755 --- a/papers/wording/generate.sh +++ b/papers/wording/generate.sh @@ -11,7 +11,8 @@ # # Produces: # papers/wording/fragments/*.tex - one fragment per top-level clause, for -# \input into a paper's own document. +# \input into a paper's own +# \rSec1[expected]{Expected objects}. # papers/wording/expected.tex - all fragments concatenated in real # standard clause order. This is the # "wording only" file: plain generated @@ -37,6 +38,7 @@ mkdir -p "$fragments_dir" echo "Generating from expected.hpp (gathers unexpected.hpp, bad_expected_access.hpp)..." >&2 specgen generate "$include_dir/beman/expected/expected.hpp" \ --backend latex --validate --no-compile-commands \ + --base-section-depth 2 \ --split "$work_dir" \ -- -std=c++2c -I "$include_dir" @@ -66,16 +68,16 @@ out="$here/expected.tex" echo '% include/beman/expected/ via specgen. Do not edit by hand: re-run' echo '% generate.sh after changing a header'\''s //! docblocks instead.' echo '%' - echo '% specgen numbers each fragment'\''s \rSec markers one level deeper than' - echo '% written in the header ([expected.unexpected] etc. render as \rSec3, their' - echo '% subclauses as \rSec4), matching the real standard'\''s absolute numbering' - echo '% (22.8.3, 22.8.3.1, ...) directly. This file is therefore everything that' - echo '% sits *inside* the existing \rSec2[expected]{Expected objects} in the' - echo '% draft'\''s source/utilities.tex, in clause order, ready to replace the' - echo '% current [expected.unexpected] through [expected.void] subclauses and add' - echo '% the new [expected.ref] one after them -- no \rSec2[expected] wrapper and' - echo '% no \input directives. [expected.general] and [expected.syn] are prose,' - echo '% not generated from any one declaration; see papers/expected-new.tex.' + echo '% --base-section-depth 2 puts each \rSec marker at the level the draft' + echo '% writes it at: [expected.unexpected] and its siblings are \rSec2, their' + echo '% subclauses \rSec3, numbering 22.8.3, 22.8.3.1, ... just as in' + echo '% source/utilities.tex. This file is therefore everything that sits' + echo '% *inside* the existing \rSec1[expected]{Expected objects} in the draft'\''s' + echo '% source/utilities.tex, in clause order, ready to replace the current' + echo '% [expected.unexpected] through [expected.void] subclauses and add the new' + echo '% [expected.ref] one after them -- no \rSec1[expected] wrapper and no' + echo '% \input directives. [expected.general] and [expected.syn] are prose, not' + echo '% generated from any one declaration; see papers/expected-new.tex.' echo sep="" for f in unexpected.tex bad.tex bad-void.tex object.tex void.tex ref.tex; do From 50115b09c3fabc000eb727ab9fc9623fc10fed0b Mon Sep 17 00:00:00 2001 From: Steve Downey Date: Wed, 16 Sep 2026 22:32:09 -0400 Subject: [PATCH 07/10] fix: drop the specgen workarounds now that #113 and #114 are fixed 2b737a4 left two workarounds in place and spelled out why each was still load-bearing. specgen has since closed both underlying issues, so both come out. specgen#113 (PR #116) adds a sibling document's \elsewhere and exposition-only using-declared names to the paper-wide documented set, so a split run no longer reports unexpect, unexpect_t, or reference_constructs_from_temporary_v as foreign. The gathered \rSec2[expected.syn] region over expected.hpp's #includes is gone: generate.sh now emits one IR per header and renders the three together with --from-ir, so validation still sees the union of documented names. specgen#114 (PR #115) stops a declaration between two clauses landing in the preceding one, so the throwaway \rSec2[expected.detail] marker is gone too. The exposition-only helpers land in the root fragments, which generate.sh already discards: expected.root.tex holds converts-from-any-cvref, is-expected-specialization and unexpect-dangles-v, expected.unexpected.root.tex holds is-unexpected-specialization. No helper leaks into a kept fragment. Two marker corrections fall out of #116 actually acting on these declarations: - unexpect_t and unexpect move from \omit to \elsewhere. They belong to the hand-authored [expected.syn], and \elsewhere is what promises that to a sibling document rather than hiding them outright. - reference_constructs_from_temporary_v moves from \expos to \elsewhere on both arms of its #ifdef. \expos on a using-declaration used to be a no-op; now that it contributes a name, every use started rendering as an exposition-only *reference-constructs-from-temporary-v*. It is the real std:: trait from [meta.rel], not a library invention, so the wording has to keep the plain spelling. With \elsewhere, fragments/unexpected.tex regenerates byte-identical to before. Also reattach [expected.ref.assign]'s trivial copy assignment docblock. A blank line had crept in between it and the declaration, silently dropping the whole itemdescr -- five paragraphs, no diagnostic. Moving the "// Copy assignment (trivial path)" comment above the docblock, as the non-trivial path a few lines down already does, restores them, and also stops that comment being swallowed into the end of the Remarks prose, which is how it read before. Regenerating leaves the section set, the itemdecl count (167) and the itemdescr count (171) unchanged. A sorted-line diff against the previous wording is exactly: the two [expected.ref.assign] \remarks merged into one, the swallowed-comment paragraph gone, and "// Copy assignment (trivial path)" added to the synopsis. The rest of the diff is declaration reordering from the #114 fix. make papers still builds D4280R0.pdf, 116 pages. Co-Authored-By: Claude Opus 5 (1M context) --- include/beman/expected/expected.hpp | 30 +- include/beman/expected/unexpected.hpp | 8 +- papers/wording/README.md | 3 + papers/wording/expected.tex | 614 +++++++++++++------------- papers/wording/fragments/object.tex | 236 +++++----- papers/wording/fragments/ref.tex | 220 +++++---- papers/wording/fragments/void.tex | 146 +++--- papers/wording/generate.sh | 67 ++- 8 files changed, 670 insertions(+), 654 deletions(-) diff --git a/include/beman/expected/expected.hpp b/include/beman/expected/expected.hpp index 82451fe..7d5faf3 100644 --- a/include/beman/expected/expected.hpp +++ b/include/beman/expected/expected.hpp @@ -3,10 +3,8 @@ #ifndef BEMAN_EXPECTED_EXPECTED_HPP #define BEMAN_EXPECTED_EXPECTED_HPP -// \rSec2[expected.syn]{Header synopsis} #include #include -/// END [expected.syn] #ifndef BEMAN_EXPECTED_INCLUDED_FROM_INTERFACE_UNIT #include @@ -58,7 +56,6 @@ namespace expected { namespace detail { -// \rSec2[expected.detail]{Exposition-only helpers} //! \expos template struct is_expected_specialization : std::false_type {}; @@ -3341,10 +3338,8 @@ class expected { // Assignment (rebind semantics) // ------------------------------------------------------------------------- + // Copy assignment (trivial path) //! \at expected.ref.assign - //! \remarks Assignment rebinds: assigning to an `expected` that - //! holds a value changes which object it refers to. It never assigns - //! through to the referent. //! \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`, @@ -3352,8 +3347,9 @@ class expected { //! 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 trivial. - // Copy assignment (trivial path) + //! \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) @@ -3604,9 +3600,6 @@ class expected { // ------------------------------------------------------------------------- //! \at expected.ref.eq - //! \remarks The equality operators behave as specified for the primary - //! template, comparing referents through `operator*` and errors - //! through `error()`. //! \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 @@ -3614,6 +3607,9 @@ class expected { //! \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) { @@ -4174,12 +4170,6 @@ constexpr typename expected::error_value_type expected::error_or(G // \rSec3[expected.ref.monadic]{Monadic operations} -//! \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. //! \group ref-monadic-and-then-lval //! \constraints `is_constructible_v` is `true`. //! \mandates `remove_cvref_t>` is a specialization @@ -4187,6 +4177,12 @@ constexpr typename expected::error_value_type expected::error_or(G //! \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 diff --git a/include/beman/expected/unexpected.hpp b/include/beman/expected/unexpected.hpp index a3f1e0b..673eb9b 100644 --- a/include/beman/expected/unexpected.hpp +++ b/include/beman/expected/unexpected.hpp @@ -25,11 +25,11 @@ namespace beman { namespace expected { // [expected.unexpect] -//! \omit +//! \elsewhere struct unexpect_t { explicit unexpect_t() = default; }; -//! \omit +//! \elsewhere inline constexpr unexpect_t unexpect{}; // Forward declaration for is_unexpected_specialization trait @@ -48,10 +48,10 @@ 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 -//! \expos +//! \elsewhere using std::reference_constructs_from_temporary_v; #elif __has_builtin(__reference_constructs_from_temporary) -//! \expos +//! \elsewhere template inline constexpr bool reference_constructs_from_temporary_v = __reference_constructs_from_temporary(T, U); #endif diff --git a/papers/wording/README.md b/papers/wording/README.md index 1af7238..693f973 100644 --- a/papers/wording/README.md +++ b/papers/wording/README.md @@ -12,6 +12,9 @@ make wording (equivalently: `papers/wording/generate.sh`, with `specgen` on `PATH`). Don't hand-edit the files here; the header comments are the source of truth. +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 — diff --git a/papers/wording/expected.tex b/papers/wording/expected.tex index bf02314..f7e2e73 100644 --- a/papers/wording/expected.tex +++ b/papers/wording/expected.tex @@ -947,32 +947,36 @@ \indexlibraryctor{expected}% \begin{itemdecl} -constexpr expected(const expected&) = default; +constexpr expected() noexcept(is_nothrow_default_constructible_v); \end{itemdecl} \begin{itemdescr} +\pnum +\constraints +\tcode{is_default_constructible_v} is \tcode{true}. + \pnum \effects -Direct-non-list-initializes \tcode{val} or \tcode{unex} (matching \tcode{rhs}'s active member) by trivial copy construction. +Value-initializes \tcode{val}. \pnum \ensures -\tcode{rhs.has_value() == this->has_value()}. +\tcode{has_value()} is \tcode{true}. \pnum -\remarks -This constructor is trivial. +\throws +Any exception thrown by the initialization of \tcode{val}. \end{itemdescr} \indexlibraryctor{expected}% \begin{itemdecl} -constexpr expected(expected&&) = default; +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 move construction. +Direct-non-list-initializes \tcode{val} or \tcode{unex} (matching \tcode{rhs}'s active member) by trivial copy construction. \pnum \ensures @@ -985,92 +989,49 @@ \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(@\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 -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} -constexpr expected() noexcept(is_nothrow_default_constructible_v); +constexpr expected(const expected& rhs) noexcept(is_nothrow_copy_constructible_v && + is_nothrow_copy_constructible_v); \end{itemdecl} \begin{itemdescr} \pnum \constraints -\tcode{is_default_constructible_v} is \tcode{true}. +\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 -Value-initializes \tcode{val}. +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{has_value()} is \tcode{true}. +\tcode{rhs.has_value() == this->has_value()}. \pnum \throws -Any exception thrown by the initialization of \tcode{val}. +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(const expected& rhs) noexcept(is_nothrow_copy_constructible_v && - is_nothrow_copy_constructible_v); +constexpr expected(expected&&) = default; \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()}. +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 -\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}. +This constructor is trivial. \end{itemdescr} \indexlibraryctor{expected}% @@ -1268,6 +1229,19 @@ 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 @@ -1343,6 +1317,19 @@ 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 @@ -1372,6 +1359,19 @@ 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}% @@ -1442,24 +1442,27 @@ \indexlibrarymember{operator=}{expected}% \begin{itemdecl} -constexpr expected& operator=(expected&&) noexcept; +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_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}. +\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 -Trivially moves \tcode{rhs}'s active member into \tcode{*this}. +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 @@ -1467,45 +1470,29 @@ \pnum \remarks -This operator is trivial. -\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. +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=(const expected& rhs) noexcept( - is_nothrow_copy_constructible_v && is_nothrow_copy_assignable_v && - is_nothrow_copy_constructible_v && is_nothrow_copy_assignable_v); +constexpr expected& operator=(expected&&) noexcept; \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}. +\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 -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. +Trivially moves \tcode{rhs}'s active member into \tcode{*this}. \pnum \returns @@ -1513,7 +1500,7 @@ \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}. +This operator is trivial. \end{itemdescr} \indexlibrarymember{operator=}{expected}% @@ -1637,6 +1624,19 @@ 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 @@ -1673,27 +1673,6 @@ \rSec3[expected.object.swap]{Swap} -\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} - \indexlibrarymember{swap}{expected}% \begin{itemdecl} constexpr void swap(expected& rhs) noexcept(is_nothrow_move_constructible_v && @@ -1721,6 +1700,27 @@ 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}% @@ -2511,63 +2511,24 @@ \indexlibraryctor{expected}% \begin{itemdecl} -template - requires(is_reference_v && !is_reference_v) -constexpr expected(const unexpected&); +constexpr expected() noexcept; \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. +\ensures +\tcode{has_value()} is \tcode{true}. \end{itemdescr} \indexlibraryctor{expected}% \begin{itemdecl} -template - requires(@\exposidnc{unexpect-dangles-v}@) -constexpr expected(unexpect_t, Args&&...); +constexpr expected(const expected& rhs) noexcept(is_nothrow_copy_constructible_v); \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 -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} -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}. +\constraints +\tcode{is_copy_constructible_v} is \tcode{true} and \tcode{is_trivially_copy_constructible_v} is \tcode{false}. \pnum \effects @@ -2709,6 +2670,19 @@ 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; @@ -2745,6 +2719,19 @@ 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 @@ -2774,6 +2761,19 @@ 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 @@ -2827,19 +2827,6 @@ \rSec3[expected.void.assign]{Assignment} -\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{operator=}{expected}% \begin{itemdecl} constexpr expected& operator=(const expected& rhs) noexcept( @@ -2949,34 +2936,32 @@ This operator never throws: the referent is bound, not copied. \end{itemdescr} -\indexlibrarymember{emplace}{expected}% +\indexlibrarymember{operator=}{expected}% \begin{itemdecl} -constexpr void emplace() noexcept; +template + requires(is_reference_v && !is_reference_v) +constexpr expected& operator=(const unexpected&); \end{itemdecl} \begin{itemdescr} \pnum -\effects -If \tcode{has_value()} is \tcode{false}, destroys \tcode{unex} and sets \tcode{has_val} to \tcode{true}. +\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} -\rSec3[expected.void.swap]{Swap} - -\indexlibraryglobal{swap}% +\indexlibrarymember{emplace}{expected}% \begin{itemdecl} -friend constexpr void swap(expected& x, expected& y) noexcept(noexcept(x.swap(y))); +constexpr void emplace() noexcept; \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)}. +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 && @@ -3002,21 +2987,23 @@ The exception specification is equivalent to \tcode{is_nothrow_move_constructible_v && (is_reference_v || is_nothrow_swappable_v)}. \end{itemdescr} -\rSec3[expected.void.obs]{Observers} - -\indexlibrarymember{value_or}{expected}% +\indexlibraryglobal{swap}% \begin{itemdecl} -template - requires is_reference_v -constexpr void value_or(U&&) const; +friend constexpr void swap(expected& x, expected& y) noexcept(noexcept(x.swap(y))); \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. +\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} @@ -3139,6 +3126,19 @@ \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}% @@ -3509,6 +3509,7 @@ // 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) @@ -3741,6 +3742,30 @@ 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; @@ -3809,69 +3834,6 @@ 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_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(@\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 -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} -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} template @@ -3975,6 +3937,19 @@ 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 @@ -3996,6 +3971,19 @@ \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 @@ -4021,6 +4009,19 @@ 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}% @@ -4083,25 +4084,26 @@ \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. - -\pnum -This operator is trivial. Copy assignment (trivial path) +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=(expected&&) noexcept; +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_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}. +\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. +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 @@ -4109,70 +4111,53 @@ \pnum \remarks -This operator is trivial. +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} -template - requires(!is_same_v, expected> && - !@\exposidnc{is-unexpected-specialization}@>::value && - is_constructible_v && !reference_constructs_from_temporary_v) -constexpr expected& operator=(U&& u); +constexpr expected& operator=(expected&&) noexcept; \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}. +\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 -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. +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}. -\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. +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); +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{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}. +\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 -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}. +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}. - -\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}% @@ -4230,6 +4215,19 @@ 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 @@ -4253,9 +4251,11 @@ \rSec3[expected.ref.swap]{Swap} -\indexlibraryglobal{swap}% +\indexlibrarymember{swap}{expected}% \begin{itemdecl} -friend constexpr void swap(expected& x, expected& y) noexcept(noexcept(x.swap(y))); +constexpr void swap(expected& rhs) noexcept(is_nothrow_move_constructible_v && + (is_reference_v || + is_nothrow_swappable_v)); \end{itemdecl} \begin{itemdescr} @@ -4265,14 +4265,16 @@ \pnum \effects -Equivalent to \tcode{x.swap(y)}. +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} -\indexlibrarymember{swap}{expected}% +\indexlibraryglobal{swap}% \begin{itemdecl} -constexpr void swap(expected& rhs) noexcept(is_nothrow_move_constructible_v && - (is_reference_v || - is_nothrow_swappable_v)); +friend constexpr void swap(expected& x, expected& y) noexcept(noexcept(x.swap(y))); \end{itemdecl} \begin{itemdescr} @@ -4282,11 +4284,7 @@ \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)}. +Equivalent to \tcode{x.swap(y)}. \end{itemdescr} \rSec3[expected.ref.obs]{Observers} diff --git a/papers/wording/fragments/object.tex b/papers/wording/fragments/object.tex index 5b5719b..87ac138 100644 --- a/papers/wording/fragments/object.tex +++ b/papers/wording/fragments/object.tex @@ -449,32 +449,36 @@ \indexlibraryctor{expected}% \begin{itemdecl} -constexpr expected(const expected&) = default; +constexpr expected() noexcept(is_nothrow_default_constructible_v); \end{itemdecl} \begin{itemdescr} +\pnum +\constraints +\tcode{is_default_constructible_v} is \tcode{true}. + \pnum \effects -Direct-non-list-initializes \tcode{val} or \tcode{unex} (matching \tcode{rhs}'s active member) by trivial copy construction. +Value-initializes \tcode{val}. \pnum \ensures -\tcode{rhs.has_value() == this->has_value()}. +\tcode{has_value()} is \tcode{true}. \pnum -\remarks -This constructor is trivial. +\throws +Any exception thrown by the initialization of \tcode{val}. \end{itemdescr} \indexlibraryctor{expected}% \begin{itemdecl} -constexpr expected(expected&&) = default; +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 move construction. +Direct-non-list-initializes \tcode{val} or \tcode{unex} (matching \tcode{rhs}'s active member) by trivial copy construction. \pnum \ensures @@ -487,92 +491,49 @@ \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(@\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 -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} -constexpr expected() noexcept(is_nothrow_default_constructible_v); +constexpr expected(const expected& rhs) noexcept(is_nothrow_copy_constructible_v && + is_nothrow_copy_constructible_v); \end{itemdecl} \begin{itemdescr} \pnum \constraints -\tcode{is_default_constructible_v} is \tcode{true}. +\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 -Value-initializes \tcode{val}. +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{has_value()} is \tcode{true}. +\tcode{rhs.has_value() == this->has_value()}. \pnum \throws -Any exception thrown by the initialization of \tcode{val}. +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(const expected& rhs) noexcept(is_nothrow_copy_constructible_v && - is_nothrow_copy_constructible_v); +constexpr expected(expected&&) = default; \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()}. +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 -\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}. +This constructor is trivial. \end{itemdescr} \indexlibraryctor{expected}% @@ -770,6 +731,19 @@ 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 @@ -845,6 +819,19 @@ 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 @@ -874,6 +861,19 @@ 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}% @@ -944,24 +944,27 @@ \indexlibrarymember{operator=}{expected}% \begin{itemdecl} -constexpr expected& operator=(expected&&) noexcept; +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_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}. +\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 -Trivially moves \tcode{rhs}'s active member into \tcode{*this}. +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 @@ -969,45 +972,29 @@ \pnum \remarks -This operator is trivial. -\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. +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=(const expected& rhs) noexcept( - is_nothrow_copy_constructible_v && is_nothrow_copy_assignable_v && - is_nothrow_copy_constructible_v && is_nothrow_copy_assignable_v); +constexpr expected& operator=(expected&&) noexcept; \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}. +\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 -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. +Trivially moves \tcode{rhs}'s active member into \tcode{*this}. \pnum \returns @@ -1015,7 +1002,7 @@ \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}. +This operator is trivial. \end{itemdescr} \indexlibrarymember{operator=}{expected}% @@ -1139,6 +1126,19 @@ 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 @@ -1175,27 +1175,6 @@ \rSec3[expected.object.swap]{Swap} -\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} - \indexlibrarymember{swap}{expected}% \begin{itemdecl} constexpr void swap(expected& rhs) noexcept(is_nothrow_move_constructible_v && @@ -1223,6 +1202,27 @@ 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}% diff --git a/papers/wording/fragments/ref.tex b/papers/wording/fragments/ref.tex index 6f6da96..fb4cda2 100644 --- a/papers/wording/fragments/ref.tex +++ b/papers/wording/fragments/ref.tex @@ -171,6 +171,7 @@ // 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) @@ -403,6 +404,30 @@ 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; @@ -471,69 +496,6 @@ 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_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(@\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 -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} -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} template @@ -637,6 +599,19 @@ 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 @@ -658,6 +633,19 @@ \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 @@ -683,6 +671,19 @@ 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}% @@ -745,25 +746,26 @@ \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. - -\pnum -This operator is trivial. Copy assignment (trivial path) +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=(expected&&) noexcept; +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_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}. +\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. +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 @@ -771,70 +773,53 @@ \pnum \remarks -This operator is trivial. +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} -template - requires(!is_same_v, expected> && - !@\exposidnc{is-unexpected-specialization}@>::value && - is_constructible_v && !reference_constructs_from_temporary_v) -constexpr expected& operator=(U&& u); +constexpr expected& operator=(expected&&) noexcept; \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}. +\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 -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. +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}. -\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. +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); +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{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}. +\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 -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}. +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}. - -\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}% @@ -892,6 +877,19 @@ 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 @@ -915,9 +913,11 @@ \rSec3[expected.ref.swap]{Swap} -\indexlibraryglobal{swap}% +\indexlibrarymember{swap}{expected}% \begin{itemdecl} -friend constexpr void swap(expected& x, expected& y) noexcept(noexcept(x.swap(y))); +constexpr void swap(expected& rhs) noexcept(is_nothrow_move_constructible_v && + (is_reference_v || + is_nothrow_swappable_v)); \end{itemdecl} \begin{itemdescr} @@ -927,14 +927,16 @@ \pnum \effects -Equivalent to \tcode{x.swap(y)}. +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} -\indexlibrarymember{swap}{expected}% +\indexlibraryglobal{swap}% \begin{itemdecl} -constexpr void swap(expected& rhs) noexcept(is_nothrow_move_constructible_v && - (is_reference_v || - is_nothrow_swappable_v)); +friend constexpr void swap(expected& x, expected& y) noexcept(noexcept(x.swap(y))); \end{itemdecl} \begin{itemdescr} @@ -944,11 +946,7 @@ \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)}. +Equivalent to \tcode{x.swap(y)}. \end{itemdescr} \rSec3[expected.ref.obs]{Observers} diff --git a/papers/wording/fragments/void.tex b/papers/wording/fragments/void.tex index e5bc3ec..51de509 100644 --- a/papers/wording/fragments/void.tex +++ b/papers/wording/fragments/void.tex @@ -337,45 +337,6 @@ \rSec3[expected.void.cons]{Constructors} -\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(@\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 -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} constexpr expected() noexcept; @@ -537,6 +498,19 @@ 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; @@ -573,6 +547,19 @@ 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 @@ -602,6 +589,19 @@ 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 @@ -655,19 +655,6 @@ \rSec3[expected.void.assign]{Assignment} -\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{operator=}{expected}% \begin{itemdecl} constexpr expected& operator=(const expected& rhs) noexcept( @@ -777,34 +764,32 @@ This operator never throws: the referent is bound, not copied. \end{itemdescr} -\indexlibrarymember{emplace}{expected}% +\indexlibrarymember{operator=}{expected}% \begin{itemdecl} -constexpr void emplace() noexcept; +template + requires(is_reference_v && !is_reference_v) +constexpr expected& operator=(const unexpected&); \end{itemdecl} \begin{itemdescr} \pnum -\effects -If \tcode{has_value()} is \tcode{false}, destroys \tcode{unex} and sets \tcode{has_val} to \tcode{true}. +\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} -\rSec3[expected.void.swap]{Swap} - -\indexlibraryglobal{swap}% +\indexlibrarymember{emplace}{expected}% \begin{itemdecl} -friend constexpr void swap(expected& x, expected& y) noexcept(noexcept(x.swap(y))); +constexpr void emplace() noexcept; \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)}. +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 && @@ -830,21 +815,23 @@ The exception specification is equivalent to \tcode{is_nothrow_move_constructible_v && (is_reference_v || is_nothrow_swappable_v)}. \end{itemdescr} -\rSec3[expected.void.obs]{Observers} - -\indexlibrarymember{value_or}{expected}% +\indexlibraryglobal{swap}% \begin{itemdecl} -template - requires is_reference_v -constexpr void value_or(U&&) const; +friend constexpr void swap(expected& x, expected& y) noexcept(noexcept(x.swap(y))); \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. +\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} @@ -967,6 +954,19 @@ \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}% diff --git a/papers/wording/generate.sh b/papers/wording/generate.sh index 09bb9cf..2cf8826 100755 --- a/papers/wording/generate.sh +++ b/papers/wording/generate.sh @@ -4,10 +4,8 @@ # # Regenerates the [expected] clause wording from the annotated headers in # include/beman/expected/, via specgen (https://github.com/steve-downey/specgen). -# expected.hpp gathers unexpected.hpp and bad_expected_access.hpp into one -# document (their #includes sit inside a \rSec2[expected.syn] ... END [expected.syn] -# region), so a single specgen invocation on expected.hpp covers all three -# headers' wording in one run. +# Each header is one specgen document. Their IR is rendered together so +# validation sees the paper-wide union of documented names. # # Produces: # papers/wording/fragments/*.tex - one fragment per top-level clause, for @@ -33,31 +31,54 @@ fragments_dir="$here/fragments" work_dir="$(mktemp -d)" trap 'rm -rf "$work_dir"' EXIT +clang_args=(-std=c++2c -I "$include_dir") +if [[ -n "${SPECGEN_GCC_TOOLCHAIN:-}" ]]; then + clang_args+=("--gcc-toolchain=$SPECGEN_GCC_TOOLCHAIN") +fi + mkdir -p "$fragments_dir" -echo "Generating from expected.hpp (gathers unexpected.hpp, bad_expected_access.hpp)..." >&2 -specgen generate "$include_dir/beman/expected/expected.hpp" \ - --backend latex --validate --no-compile-commands \ +generate_ir() { + local header="$1" + local ir="$2" + + echo "Generating IR from $header..." >&2 + specgen generate "$include_dir/beman/expected/$header" \ + --emit-ir --no-compile-commands \ + -o "$work_dir/$ir" \ + -- "${clang_args[@]}" +} + +generate_ir unexpected.hpp unexpected.json +generate_ir bad_expected_access.hpp bad_expected_access.json +generate_ir expected.hpp expected.json + +render_dir="$work_dir/rendered" +echo "Rendering and validating the three-header paper..." >&2 +specgen render \ + --from-ir "$work_dir/unexpected.json" \ + --from-ir "$work_dir/bad_expected_access.json" \ + --from-ir "$work_dir/expected.json" \ + --root expected.unexpected.root \ + --root expected.bad.root \ + --root expected.root \ + --backend latex --validate \ --base-section-depth 2 \ - --split "$work_dir" \ - -- -std=c++2c -I "$include_dir" + --split "$render_dir" # Map specgen's stable-name-derived filenames to the fragment names we keep. -cp "$work_dir/expected.unexpected.tex" "$fragments_dir/unexpected.tex" -cp "$work_dir/expected.bad.tex" "$fragments_dir/bad.tex" -cp "$work_dir/expected.bad.void.tex" "$fragments_dir/bad-void.tex" -cp "$work_dir/expected.expected.tex" "$fragments_dir/object.tex" -cp "$work_dir/expected.void.tex" "$fragments_dir/void.tex" -cp "$work_dir/expected.ref.tex" "$fragments_dir/ref.tex" +cp "$render_dir/expected.unexpected.tex" "$fragments_dir/unexpected.tex" +cp "$render_dir/expected.bad.tex" "$fragments_dir/bad.tex" +cp "$render_dir/expected.bad.void.tex" "$fragments_dir/bad-void.tex" +cp "$render_dir/expected.expected.tex" "$fragments_dir/object.tex" +cp "$render_dir/expected.void.tex" "$fragments_dir/void.tex" +cp "$render_dir/expected.ref.tex" "$fragments_dir/ref.tex" -# expected.syn.tex (the header synopsis, gathered from the two #includes) and -# expected.detail.tex (a throwaway \rSec2 fencing off the exposition-only -# helpers -- is_expected_specialization, reinit_expected, unexpect_dangles_v, -# converts_from_any_cvref -- declared above [expected.expected] so they don't -# bleed into [expected.bad]) are not part of the standard's own wording (the -# real draft states an equivalent helper, reinit-expected, inline in -# [expected.object.assign]'s own intro instead) and are intentionally omitted -# from the assembled clause below. +# Any root fragments contain declarations outside generated clauses, including +# exposition-only implementation helpers. They are not part of the standard's +# own wording (the real draft states an equivalent helper, reinit-expected, +# inline in [expected.object.assign]'s own intro instead) and are intentionally +# omitted from the assembled clause below. out="$here/expected.tex" { From e38a6f2af461a78225aa5d2bd27ae02f084092cc Mon Sep 17 00:00:00 2001 From: Steve Downey Date: Fri, 18 Sep 2026 12:50:01 -0400 Subject: [PATCH 08/10] Build the wording with make, not a script papers/wording/generate.sh existed because specgen could not express a three-header paper in one command: it ran three `generate --emit-ir` invocations into a temporary directory, one `render --split`, renamed the fragments, dropped the ones it did not want, and concatenated the rest. And nothing depended on any of it -- `wording` was .PHONY with no prerequisites and `papers` did not depend on it at all, so the PDF could be built from wording three commits stale and look exactly like a PDF built from current wording. specgen now takes the whole paper in one invocation and reports what it read, so the script becomes a makefile rule with real prerequisites: - one `specgen generate` over all three headers, so `--validate` sees the paper-wide union of documented names in a single parse (~2s); - `--depfile` writes what it read to papers/.deps/wording.d, which the makefile `-include`s. That list has include/beman/expected/config.hpp in it -- a header none of the three names on the command line, which the old script had no way to know about and which a hand-written prerequisite list would forget; - `make papers` now depends on `wording`, closing the last open edge: headers -> wording -> PDF. The fragments keep their stable names (expected.bad.void.tex rather than bad-void.tex), which deletes the rename step; nothing `\input`s them, so this costs nothing. The regenerated fragments are byte-identical to the ones they replace, and expected.tex differs only in the comment at its top, which now lives in papers/wording/preamble.tex. What did not move into specgen is the clause order. bad_expected_access is the base class of bad_expected_access, so the header has to define the specialization first, while the draft states the primary template first -- the paper's order is not the headers' and cannot be derived from them. $(WORDING_CLAUSES) is where that one divergence is now written down, instead of being implicit in the order a script happened to cat six files. Requires specgen with multi-header `generate` and `--depfile`. Co-Authored-By: Claude Opus 5 (1M context) --- .gitignore | 6 + Makefile | 78 +++++++++++- papers/wording/README.md | 46 +++++-- papers/wording/expected.tex | 7 +- .../fragments/{bad.tex => expected.bad.tex} | 0 .../{bad-void.tex => expected.bad.void.tex} | 0 .../{object.tex => expected.expected.tex} | 0 .../fragments/{ref.tex => expected.ref.tex} | 0 ...unexpected.tex => expected.unexpected.tex} | 0 .../fragments/{void.tex => expected.void.tex} | 0 papers/wording/generate.sh | 112 ------------------ papers/wording/preamble.tex | 18 +++ 12 files changed, 139 insertions(+), 128 deletions(-) rename papers/wording/fragments/{bad.tex => expected.bad.tex} (100%) rename papers/wording/fragments/{bad-void.tex => expected.bad.void.tex} (100%) rename papers/wording/fragments/{object.tex => expected.expected.tex} (100%) rename papers/wording/fragments/{ref.tex => expected.ref.tex} (100%) rename papers/wording/fragments/{unexpected.tex => expected.unexpected.tex} (100%) rename papers/wording/fragments/{void.tex => expected.void.tex} (100%) delete mode 100755 papers/wording/generate.sh create mode 100644 papers/wording/preamble.tex 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 1b41033..1dee3e3 100755 --- a/Makefile +++ b/Makefile @@ -154,12 +154,84 @@ 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: ## Regenerate papers/wording/ from the annotated headers via specgen - papers/wording/generate.sh +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, which is why +# this is -include and not include. +-include papers/.deps/*.d .DEFAULT: $(_build_path)/CMakeCache.txt ## Other targets passed through to cmake $(CMAKE) --build $(_build_path) --config $(CONFIG) --target $@ -- -k 0 diff --git a/papers/wording/README.md b/papers/wording/README.md index 693f973..cf64abc 100644 --- a/papers/wording/README.md +++ b/papers/wording/README.md @@ -2,15 +2,25 @@ 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). Regenerate after editing a -header's docblocks with: +[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 ``` -(equivalently: `papers/wording/generate.sh`, with `specgen` on `PATH`). Don't -hand-edit the files here; the header comments are the source of truth. +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. + +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. @@ -19,7 +29,7 @@ If embedded Clang cannot locate the C++ standard library, set All the generated subclauses concatenated in real standard order — `[expected.unexpected]` (including the new `[expected.un.ref]`) through -`[expected.ref.eq]`. `generate.sh` passes `--base-section-depth 2`, so each +`[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 @@ -34,10 +44,26 @@ 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 -(`unexpected.tex`, `bad.tex`, `bad-void.tex`, `object.tex`, `void.tex`, -`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. +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 index f7e2e73..d94fef4 100644 --- a/papers/wording/expected.tex +++ b/papers/wording/expected.tex @@ -1,9 +1,10 @@ % papers/wording/expected.tex -*-LaTeX-*- % SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception % -% Generated by papers/wording/generate.sh from the annotated headers in -% include/beman/expected/ via specgen. Do not edit by hand: re-run -% generate.sh after changing a header's //! docblocks instead. +% 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 diff --git a/papers/wording/fragments/bad.tex b/papers/wording/fragments/expected.bad.tex similarity index 100% rename from papers/wording/fragments/bad.tex rename to papers/wording/fragments/expected.bad.tex diff --git a/papers/wording/fragments/bad-void.tex b/papers/wording/fragments/expected.bad.void.tex similarity index 100% rename from papers/wording/fragments/bad-void.tex rename to papers/wording/fragments/expected.bad.void.tex diff --git a/papers/wording/fragments/object.tex b/papers/wording/fragments/expected.expected.tex similarity index 100% rename from papers/wording/fragments/object.tex rename to papers/wording/fragments/expected.expected.tex diff --git a/papers/wording/fragments/ref.tex b/papers/wording/fragments/expected.ref.tex similarity index 100% rename from papers/wording/fragments/ref.tex rename to papers/wording/fragments/expected.ref.tex diff --git a/papers/wording/fragments/unexpected.tex b/papers/wording/fragments/expected.unexpected.tex similarity index 100% rename from papers/wording/fragments/unexpected.tex rename to papers/wording/fragments/expected.unexpected.tex diff --git a/papers/wording/fragments/void.tex b/papers/wording/fragments/expected.void.tex similarity index 100% rename from papers/wording/fragments/void.tex rename to papers/wording/fragments/expected.void.tex diff --git a/papers/wording/generate.sh b/papers/wording/generate.sh deleted file mode 100755 index 2cf8826..0000000 --- a/papers/wording/generate.sh +++ /dev/null @@ -1,112 +0,0 @@ -#!/usr/bin/env bash -# papers/wording/generate.sh -*-sh-*- -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -# -# Regenerates the [expected] clause wording from the annotated headers in -# include/beman/expected/, via specgen (https://github.com/steve-downey/specgen). -# Each header is one specgen document. Their IR is rendered together so -# validation sees the paper-wide union of documented names. -# -# Produces: -# papers/wording/fragments/*.tex - one fragment per top-level clause, for -# \input into a paper's own -# \rSec1[expected]{Expected objects}. -# papers/wording/expected.tex - all fragments concatenated in real -# standard clause order. This is the -# "wording only" file: plain generated -# prose, with no \input directives, -# suitable as the basis for a diff -# against the actual draft -# (github.com/cplusplus/draft) -# source/utilities.tex. -# -# specgen must be on PATH. Run from anywhere; paths below are relative to -# this script's location. -set -euo pipefail - -here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -repo_root="$(cd "$here/../.." && pwd)" -include_dir="$repo_root/include" -fragments_dir="$here/fragments" -work_dir="$(mktemp -d)" -trap 'rm -rf "$work_dir"' EXIT - -clang_args=(-std=c++2c -I "$include_dir") -if [[ -n "${SPECGEN_GCC_TOOLCHAIN:-}" ]]; then - clang_args+=("--gcc-toolchain=$SPECGEN_GCC_TOOLCHAIN") -fi - -mkdir -p "$fragments_dir" - -generate_ir() { - local header="$1" - local ir="$2" - - echo "Generating IR from $header..." >&2 - specgen generate "$include_dir/beman/expected/$header" \ - --emit-ir --no-compile-commands \ - -o "$work_dir/$ir" \ - -- "${clang_args[@]}" -} - -generate_ir unexpected.hpp unexpected.json -generate_ir bad_expected_access.hpp bad_expected_access.json -generate_ir expected.hpp expected.json - -render_dir="$work_dir/rendered" -echo "Rendering and validating the three-header paper..." >&2 -specgen render \ - --from-ir "$work_dir/unexpected.json" \ - --from-ir "$work_dir/bad_expected_access.json" \ - --from-ir "$work_dir/expected.json" \ - --root expected.unexpected.root \ - --root expected.bad.root \ - --root expected.root \ - --backend latex --validate \ - --base-section-depth 2 \ - --split "$render_dir" - -# Map specgen's stable-name-derived filenames to the fragment names we keep. -cp "$render_dir/expected.unexpected.tex" "$fragments_dir/unexpected.tex" -cp "$render_dir/expected.bad.tex" "$fragments_dir/bad.tex" -cp "$render_dir/expected.bad.void.tex" "$fragments_dir/bad-void.tex" -cp "$render_dir/expected.expected.tex" "$fragments_dir/object.tex" -cp "$render_dir/expected.void.tex" "$fragments_dir/void.tex" -cp "$render_dir/expected.ref.tex" "$fragments_dir/ref.tex" - -# Any root fragments contain declarations outside generated clauses, including -# exposition-only implementation helpers. They are not part of the standard's -# own wording (the real draft states an equivalent helper, reinit-expected, -# inline in [expected.object.assign]'s own intro instead) and are intentionally -# omitted from the assembled clause below. - -out="$here/expected.tex" -{ - echo '% papers/wording/expected.tex -*-LaTeX-*-' - echo '% SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception' - echo '%' - echo '% Generated by papers/wording/generate.sh from the annotated headers in' - echo '% include/beman/expected/ via specgen. Do not edit by hand: re-run' - echo '% generate.sh after changing a header'\''s //! docblocks instead.' - echo '%' - echo '% --base-section-depth 2 puts each \rSec marker at the level the draft' - echo '% writes it at: [expected.unexpected] and its siblings are \rSec2, their' - echo '% subclauses \rSec3, numbering 22.8.3, 22.8.3.1, ... just as in' - echo '% source/utilities.tex. This file is therefore everything that sits' - echo '% *inside* the existing \rSec1[expected]{Expected objects} in the draft'\''s' - echo '% source/utilities.tex, in clause order, ready to replace the current' - echo '% [expected.unexpected] through [expected.void] subclauses and add the new' - echo '% [expected.ref] one after them -- no \rSec1[expected] wrapper and no' - echo '% \input directives. [expected.general] and [expected.syn] are prose, not' - echo '% generated from any one declaration; see papers/expected-new.tex.' - echo - sep="" - for f in unexpected.tex bad.tex bad-void.tex object.tex void.tex ref.tex; do - printf '%s' "$sep" - cat "$fragments_dir/$f" - sep=$'\n' - done -} > "$out" - -echo "Wrote $out" >&2 -echo "Wrote $fragments_dir/{unexpected,bad,bad-void,object,void,ref}.tex" >&2 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. From 8cad3462ff1f77d766ad65a634cfb609bf268cc5 Mon Sep 17 00:00:00 2001 From: Steve Downey Date: Fri, 18 Sep 2026 14:59:56 -0400 Subject: [PATCH 09/10] fix: include the wording depfile by name, not by glob `-include papers/.deps/*.d` is a bare glob, unlike the three other depfile includes in this makefile, which all wrap theirs in $(wildcard). Two things follow from that. When the glob matches nothing, make keeps it as a literal target name and tries to remake it -- through `.DEFAULT`, which hands it to cmake. The depfile is only written by an actual regeneration, so on a fresh clone, where the committed fragments are already up to date, it is never written: every `make` invocation, `make help` included, first runs `uv run cmake --build ... --target 'papers/.deps/*.d'` and prints an error about it. `-include` swallows the failure, so nothing breaks; it is just noise that never goes away on its own. And papers/.deps/ is not ours alone -- papers/Makefile already points latexmk's -deps-out at it. Once anyone builds the PDF, the glob pulls D4280R0.pdf.d into the top-level makefile, where its papers/-relative paths (../include/..., ./wg21.bib) resolve against the repository root instead: 293 phantom targets at paths that do not exist, and `make D4280R0.pdf` at the root answering "nothing to be done" rather than falling through to cmake. $(wildcard) fixes the first, naming wording.d fixes the second. Also record the GNU Make floor in papers/wording/README.md. The one-invocation rule is a grouped target, which 4.3 introduced; 4.2 parses `&:` as something else and would run specgen once per fragment. Co-Authored-By: Claude Opus 5 (1M context) --- Makefile | 8 +++++--- papers/wording/README.md | 4 ++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 1dee3e3..4f1ff44 100755 --- a/Makefile +++ b/Makefile @@ -229,9 +229,11 @@ wording: $(WORDING_DIR)/expected.tex ## Regenerate papers/wording/ from the anno # 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, which is why -# this is -include and not include. --include papers/.deps/*.d +# 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/papers/wording/README.md b/papers/wording/README.md index cf64abc..0706e00 100644 --- a/papers/wording/README.md +++ b/papers/wording/README.md @@ -16,6 +16,10 @@ 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 From cb94b27a10fd6725a4d3bea118f1bf0078e7d501 Mon Sep 17 00:00:00 2001 From: Steve Downey Date: Fri, 18 Sep 2026 22:44:31 -0400 Subject: [PATCH 10/10] fix: let specgen infer declaration conditions --- include/beman/expected/expected.hpp | 11 ----------- papers/wording/expected.tex | 10 ++++++++-- papers/wording/fragments/expected.expected.tex | 8 +++++++- papers/wording/fragments/expected.void.tex | 2 +- 4 files changed, 16 insertions(+), 15 deletions(-) diff --git a/include/beman/expected/expected.hpp b/include/beman/expected/expected.hpp index 7d5faf3..c63cc0f 100644 --- a/include/beman/expected/expected.hpp +++ b/include/beman/expected/expected.hpp @@ -1162,11 +1162,6 @@ constexpr T& expected::emplace(std::initializer_list il, Args&&... args // \rSec3[expected.object.swap]{Swap} -//! \constraints `is_swappable_v` is `true` and (`is_reference_v` or -//! `is_swappable_v`) is `true`, and `is_move_constructible_v && -//! is_move_constructible_v` is `true`, and -//! `is_nothrow_move_constructible_v || -//! is_nothrow_move_constructible_v` is `true`. //! \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, @@ -1418,8 +1413,6 @@ constexpr E&& expected::error() && noexcept { return std::move(unex_).error(); } -//! \mandates `is_copy_constructible_v` is `true` and -//! `is_convertible_v` is `true`. //! \returns `has_value() ? **this : static_cast(std::forward(def))`. template template @@ -1431,8 +1424,6 @@ constexpr T expected::value_or(U&& def) const& { return static_cast(std::forward(def)); } -//! \mandates `is_move_constructible_v` is `true` and -//! `is_convertible_v` is `true`. //! \returns `has_value() ? std::move(**this) : //! static_cast(std::forward(def))`. template @@ -2579,8 +2570,6 @@ constexpr void expected::emplace() noexcept { // ============================================================================= // \rSec3[expected.void.swap]{Swap} -//! \constraints `is_swappable_v` is `true` and -//! `is_move_constructible_v` is `true`. //! \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` diff --git a/papers/wording/expected.tex b/papers/wording/expected.tex index d94fef4..ead4115 100644 --- a/papers/wording/expected.tex +++ b/papers/wording/expected.tex @@ -1686,7 +1686,13 @@ \begin{itemdescr} \pnum \constraints -\tcode{is_swappable_v} is \tcode{true} and (\tcode{is_reference_v} or \tcode{is_swappable_v}) is \tcode{true}, and \tcode{is_move_constructible_v && is_move_constructible_v} is \tcode{true}, and \tcode{is_nothrow_move_constructible_v || is_nothrow_move_constructible_v} is \tcode{true}. +\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 @@ -2973,7 +2979,7 @@ \begin{itemdescr} \pnum \constraints -\tcode{is_swappable_v} is \tcode{true} and \tcode{is_move_constructible_v} is \tcode{true}. +\tcode{is_reference_v || is_swappable_v} is \tcode{true} and \tcode{is_move_constructible_v} is \tcode{true}. \pnum \effects diff --git a/papers/wording/fragments/expected.expected.tex b/papers/wording/fragments/expected.expected.tex index 87ac138..6fcff0a 100644 --- a/papers/wording/fragments/expected.expected.tex +++ b/papers/wording/fragments/expected.expected.tex @@ -1187,7 +1187,13 @@ \begin{itemdescr} \pnum \constraints -\tcode{is_swappable_v} is \tcode{true} and (\tcode{is_reference_v} or \tcode{is_swappable_v}) is \tcode{true}, and \tcode{is_move_constructible_v && is_move_constructible_v} is \tcode{true}, and \tcode{is_nothrow_move_constructible_v || is_nothrow_move_constructible_v} is \tcode{true}. +\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 diff --git a/papers/wording/fragments/expected.void.tex b/papers/wording/fragments/expected.void.tex index 51de509..34ca1b5 100644 --- a/papers/wording/fragments/expected.void.tex +++ b/papers/wording/fragments/expected.void.tex @@ -800,7 +800,7 @@ \begin{itemdescr} \pnum \constraints -\tcode{is_swappable_v} is \tcode{true} and \tcode{is_move_constructible_v} is \tcode{true}. +\tcode{is_reference_v || is_swappable_v} is \tcode{true} and \tcode{is_move_constructible_v} is \tcode{true}. \pnum \effects