| /* An <expected> for -*- C++ -*- 14. | |
| Derived, in part, from the libstdc++ one, but mostly distinct. | |
| Copyright (C) 2026 The GNU Toolchain Authors. | |
| This file is part of GCC. | |
| GCC is free software; you can redistribute it and/or modify it under | |
| the terms of the GNU General Public License as published by the Free | |
| Software Foundation; either version 3, or (at your option) any later | |
| version. | |
| GCC is distributed in the hope that it will be useful, but WITHOUT ANY | |
| WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| for more details. | |
| You should have received a copy of the GNU General Public License | |
| along with GCC; see the file COPYING3. If not see | |
| <http://www.gnu.org/licenses/>. */ | |
| #ifndef GCC_EXPECTED | |
| #define GCC_EXPECTED 1 | |
| #include <utility> | |
| #include <type_traits> | |
| #include <initializer_list> | |
| #include <memory> | |
| #include "gbits/assert.h" | |
| #include "gbits/enable_special_members.h" | |
| #include "gbits/exception_defines.h" | |
| #include "gbits/extras.h" | |
| #include "gbits/inplace_tags.h" | |
| #include "gbits/invoke.h" | |
| #include "gbits/stl_construct.h" | |
| #ifdef GCC_BACKPORT_WANT_EXCEPTIONS | |
| # include <exception> | |
| #endif | |
| #include "expectedfwd" | |
| namespace gcc | |
| { | |
| // [expected] | |
| template<typename E> | |
| struct unexpected; | |
| struct unexpect_t | |
| { | |
| explicit unexpect_t () = default; | |
| }; | |
| static constexpr unexpect_t unexpect{}; | |
| namespace detail { namespace exp | |
| { | |
| template<typename T> | |
| struct is_unexpected : std::false_type {}; | |
| template<typename E> | |
| struct is_unexpected<unexpected<E>> : std::true_type {}; | |
| template<typename T> | |
| struct is_expected : std::false_type {}; | |
| template<typename T, typename E> | |
| struct is_expected<expected<T, E>> : std::true_type {}; | |
| // true_type if E can be contained in an unexpected{} or !has_value | |
| // expected. | |
| template<typename E> | |
| using can_be_unexpected_val = and_< | |
| std::is_object<E>, | |
| not_<std::is_array<E>>, | |
| not_<is_unexpected<E>>, | |
| not_<is_cv_qualified<E>> | |
| >; | |
| // true_type if non-void T can be contained in a has_value expected. | |
| template<typename T> | |
| using can_be_expected_val_2 = and_< | |
| std::is_object<T>, | |
| not_<std::is_array<T>>, | |
| not_<is_in_place<T>>, | |
| not_<is_unexpected<T>>, | |
| not_<std::is_same<T, unexpect_t>> | |
| >; | |
| template<typename T> | |
| using can_be_expected_val = can_be_expected_val_2<std::remove_cv_t<T>>; | |
| // 'bool' if T1 is equality-comparable with T2, and E1 with E2, SFINAE-fail | |
| // otherwise. Used for comparisons of expected types. | |
| template<typename T1, typename T2, | |
| typename E1, typename E2> | |
| using eql_2_return_t = detail::relop_return_t< | |
| decltype (std::declval<const T1&> () == std::declval<const T2&> ()), | |
| decltype (std::declval<const E1&> () == std::declval<const E2&> ()) | |
| >; | |
| // Implements the "U is a specialization of expected and is_same_v<typename | |
| // U::error_type, E> is true." mandate where U=Candidate and E=WantedError. | |
| template<typename Candidate, typename WantedError> | |
| struct is_expected_with_error : std::false_type {}; | |
| template<typename T, typename WantedError> | |
| struct is_expected_with_error<expected<T, WantedError>, WantedError> | |
| : std::true_type {}; | |
| // Implements the "G is a specialization of expected and | |
| // is_same_v<typename G::value_type, T> is true." mandate, where | |
| // G=Candidate and T=WantedValue. | |
| template<typename Candidate, typename WantedValue> | |
| struct is_expected_with_value : std::false_type {}; | |
| template<typename E, typename WantedValue> | |
| struct is_expected_with_value<expected<WantedValue, E>, WantedValue> | |
| : std::true_type {}; | |
| // Shorthand. | |
| template<template<typename> class Trait, typename T, typename E> | |
| using trait_2 = and_<Trait<T>, Trait<E>>; | |
| // Tag type, use supplied invocable to populate 'val'. | |
| struct in_place_inv {}; | |
| // Likewise, but for 'unex'. | |
| struct unexpect_inv {}; | |
| }} // namespace detail::exp | |
| template<typename E> | |
| struct unexpected | |
| { | |
| // [expected.un.general]p2 | |
| static_assert (detail::exp::can_be_unexpected_val<E>::value, | |
| "E not a valid type for unexpected<>"); | |
| // [expected.un.cons] | |
| unexpected (const unexpected &) = default; | |
| unexpected (unexpected &&) = default; | |
| template<typename Err = E, | |
| typename = std::enable_if_t<and_< | |
| std::is_constructible<E, Err>, | |
| not_<is_in_place<Err>>, | |
| not_<std::is_same<remove_cvref_t<Err>, unexpected>> | |
| >::value>> | |
| constexpr explicit | |
| unexpected (Err &&err) | |
| : unex (std::forward<Err>(err)) | |
| {} | |
| template<typename... Args, | |
| typename = std::enable_if_t< | |
| std::is_constructible<E, Args...>::value | |
| >> | |
| constexpr explicit | |
| unexpected (in_place_t, Args&&... args) | |
| : unex (std::forward<Args>(args)...) | |
| {} | |
| template<typename U, typename... Args, | |
| typename = std::enable_if_t< | |
| std::is_constructible<E, std::initializer_list<U>&, Args...>::value | |
| >> | |
| constexpr explicit unexpected (in_place_t, | |
| std::initializer_list<U> il, Args&&... args) | |
| : unex (il, std::forward<Args>(args)...) | |
| {} | |
| unexpected& operator= (const unexpected&) = default; | |
| unexpected& operator= (unexpected&&) = default; | |
| // [expected.un.obs] | |
| [[nodiscard]] constexpr const E & | |
| error () const & noexcept | |
| { return unex; } | |
| [[nodiscard]] constexpr E & | |
| error () & noexcept | |
| { return unex; } | |
| [[nodiscard]] constexpr E && | |
| error () && noexcept | |
| { return std::move (unex); } | |
| [[nodiscard]] constexpr const E && | |
| error () const && noexcept | |
| { return std::move (unex); } | |
| // [expected.un.swap] | |
| template<typename E_ = E> | |
| constexpr std::enable_if_t<g_is_swappable<E_>::value> | |
| swap (unexpected& other) noexcept (g_is_nothrow_swappable<E>::value) | |
| { | |
| using std::swap; | |
| swap (unex, other.unex); | |
| } | |
| template<typename E_ = E> | |
| friend constexpr std::enable_if_t<g_is_swappable<E_>::value> | |
| swap (unexpected &x, unexpected &y) noexcept (noexcept (x.swap (y))) | |
| { x.swap (y); } | |
| // [expected.un.eq] | |
| template<typename E2> | |
| [[nodiscard]] friend constexpr eql_return_t<E, E2> | |
| operator== (const unexpected &x, const unexpected<E2> &y) | |
| { return x.error () == y.error (); } | |
| private: | |
| template<typename, typename, bool> | |
| friend struct expected; | |
| E unex; | |
| }; | |
| // EXTENSION: Permit deduction of 'unexpected' from value type. Eventually | |
| // to be replaced with a deduction guide. | |
| template<typename E> | |
| constexpr unexpected<std::decay_t<E>> | |
| make_unexpected (E &&e) | |
| { return unexpected<std::decay_t<E>> (std::forward<E> (e)); } | |
| #if __cpp_deduction_guides >= 201703L | |
| template<class E> unexpected (E) -> unexpected<E>; | |
| #endif | |
| #ifdef GCC_BACKPORT_WANT_EXCEPTIONS | |
| // Only implemented to permit running testsuites for <expected> against this | |
| // implementation. Never actually enabled. | |
| // [expected.void.bad] | |
| template<typename E> | |
| class bad_expected_access; | |
| #if __cpp_lib_constexpr_exceptions >= 202502L | |
| #define GCC_BACKPORT_CONSTEXPR_BAD_EXPECTED_ACCESS constexpr | |
| #else | |
| #define GCC_BACKPORT_CONSTEXPR_BAD_EXPECTED_ACCESS | |
| #endif | |
| template<> | |
| class bad_expected_access<void> : public std::exception | |
| { | |
| protected: | |
| GCC_BACKPORT_CONSTEXPR_BAD_EXPECTED_ACCESS bad_expected_access () noexcept { } | |
| GCC_BACKPORT_CONSTEXPR_BAD_EXPECTED_ACCESS | |
| bad_expected_access (const bad_expected_access&) noexcept = default; | |
| GCC_BACKPORT_CONSTEXPR_BAD_EXPECTED_ACCESS | |
| bad_expected_access (bad_expected_access&&) noexcept = default; | |
| GCC_BACKPORT_CONSTEXPR_BAD_EXPECTED_ACCESS | |
| bad_expected_access& operator= (const bad_expected_access&) noexcept = default; | |
| GCC_BACKPORT_CONSTEXPR_BAD_EXPECTED_ACCESS | |
| bad_expected_access& operator= (bad_expected_access&&) noexcept = default; | |
| GCC_BACKPORT_CONSTEXPR_BAD_EXPECTED_ACCESS | |
| ~bad_expected_access () = default; | |
| public: | |
| [[nodiscard]] | |
| GCC_BACKPORT_CONSTEXPR_BAD_EXPECTED_ACCESS const char* | |
| what () const noexcept override | |
| { return "bad access to std::expected without expected value"; } | |
| }; | |
| // [expected.bad] | |
| template<typename E> | |
| class bad_expected_access : public bad_expected_access<void> { | |
| public: | |
| GCC_BACKPORT_CONSTEXPR_BAD_EXPECTED_ACCESS explicit | |
| bad_expected_access (E g_e) : m_unex (std::move (g_e)) { } | |
| // XXX const char* what () const noexcept override; | |
| [[nodiscard]] | |
| GCC_BACKPORT_CONSTEXPR_BAD_EXPECTED_ACCESS E& | |
| error () & noexcept | |
| { return m_unex; } | |
| [[nodiscard]] | |
| GCC_BACKPORT_CONSTEXPR_BAD_EXPECTED_ACCESS const E& | |
| error () const & noexcept | |
| { return m_unex; } | |
| [[nodiscard]] | |
| GCC_BACKPORT_CONSTEXPR_BAD_EXPECTED_ACCESS E&& | |
| error () && noexcept | |
| { return std::move (m_unex); } | |
| [[nodiscard]] | |
| GCC_BACKPORT_CONSTEXPR_BAD_EXPECTED_ACCESS const E&& | |
| error () const && noexcept | |
| { return std::move (m_unex); } | |
| private: | |
| E m_unex; | |
| }; | |
| #undef GCC_BACKPORT_CONSTEXPR_BAD_EXPECTED_ACCESS | |
| #endif // GCC_BACKPORT_WANT_EXCEPTIONS | |
| namespace detail { namespace exp | |
| { | |
| struct in_value_t {}; | |
| // Tag, construct value field. | |
| static constexpr in_value_t in_value; | |
| struct in_unex_t {}; | |
| // Tag, construct unex field. | |
| static constexpr in_unex_t in_unex; | |
| struct in_unused_t {}; | |
| // Tag, construct unused field. | |
| static constexpr in_unused_t in_unused; | |
| // Replaces T=void for storage, and acts as a placeholder when storage is | |
| // empty. | |
| struct void_storage {}; | |
| #define GCC_EXP_STORAGE_2_COMMON \ | |
| constexpr storage_2 () = default; \ | |
| \ | |
| template<typename... Args> \ | |
| constexpr storage_2 (in_value_t, Args&&... args) \ | |
| : m_value (std::forward<Args> (args)...) \ | |
| , m_has_value (true) \ | |
| {} \ | |
| \ | |
| template<typename... Args> \ | |
| constexpr storage_2 (in_unex_t, Args&&... args) \ | |
| : m_unex (std::forward<Args> (args)...) \ | |
| , m_has_value (false) \ | |
| {} \ | |
| \ | |
| constexpr storage_2 (in_unused_t) \ | |
| : m_unused () \ | |
| /* To appease constexpr; always overwritten. */ \ | |
| , m_has_value (false) \ | |
| {} \ | |
| \ | |
| template<typename Fn> \ | |
| constexpr storage_2 (in_place_inv, Fn &&fn) \ | |
| : m_value (std::forward<Fn> (fn) ()) \ | |
| , m_has_value (true) \ | |
| {} \ | |
| \ | |
| template<typename Fn> \ | |
| constexpr storage_2 (unexpect_inv, Fn &&fn) \ | |
| : m_unex (std::forward<Fn> (fn) ()) \ | |
| , m_has_value (false) \ | |
| {} \ | |
| \ | |
| GCC_BACKPORT20_CONSTEXPR void \ | |
| clear_storage () \ | |
| { \ | |
| if (this->m_has_value) \ | |
| gcc::destroy_at (std::addressof (this->m_value)); \ | |
| else \ | |
| gcc::destroy_at (std::addressof (this->m_unex)); \ | |
| } | |
| // Container holding the contents of the expected. Provides the | |
| // destructor. | |
| template<typename T, typename E, | |
| bool is_trivial = trait_2<std::is_trivially_destructible, | |
| T, E>::value> | |
| struct storage_2 | |
| { | |
| /* Non-trivially destructible T or E. Need a hand-written | |
| destructor. */ | |
| GCC_BACKPORT20_CONSTEXPR | |
| ~storage_2 () | |
| { clear_storage (); } | |
| GCC_EXP_STORAGE_2_COMMON | |
| union { | |
| void_storage m_unused; | |
| std::remove_cv_t<T> m_value; | |
| E m_unex; | |
| }; | |
| bool m_has_value; | |
| }; | |
| template<typename T, typename E> | |
| struct storage_2<T, E, true> | |
| { | |
| GCC_EXP_STORAGE_2_COMMON | |
| union { | |
| void_storage m_unused; | |
| std::remove_cv_t<T> m_value; | |
| E m_unex; | |
| }; | |
| bool m_has_value; | |
| }; | |
| #undef GCC_EXP_STORAGE_2_COMMON | |
| // This layer provides some utilities. | |
| template<typename T, typename E> | |
| struct storage_1 : storage_2<T, E> | |
| { | |
| using storage_2<T, E>::storage_2; | |
| // Getters preserving const-ness and ref-ness of *this. | |
| #define GCC_EXP_STORAGE_1_DO_GETTER(type, member, name) \ | |
| constexpr type &name () & noexcept \ | |
| { return this->member; } \ | |
| constexpr const type &name () const & noexcept \ | |
| { return this->member; } \ | |
| constexpr type &&name () && noexcept \ | |
| { return std::move (this->member); } \ | |
| constexpr const type &&name () const && noexcept \ | |
| { return std::move (this->member); } | |
| GCC_EXP_STORAGE_1_DO_GETTER (T, m_value, value) | |
| GCC_EXP_STORAGE_1_DO_GETTER (E, m_unex, unex) | |
| #undef GCC_EXP_STORAGE_1_DO_GETTER | |
| // Helpers, for initial construction. | |
| template<typename... Args> | |
| constexpr void | |
| construct_value (Args&&... args) | |
| { | |
| gcc::construct_at (std::addressof (this->m_value), | |
| std::forward<Args> (args)...); | |
| this->m_has_value = true; | |
| } | |
| template<typename... Args> | |
| constexpr void | |
| construct_unex (Args&&... args) | |
| { | |
| gcc::construct_at (std::addressof (this->m_unex), | |
| std::forward<Args> (args)...); | |
| this->m_has_value = false; | |
| } | |
| // Copy/move construct our value/unex field based on other storage. | |
| // Presumes 'm_unused' is active right now. | |
| template<typename Other> | |
| constexpr | |
| std::enable_if_t<std::is_convertible<std::remove_reference_t<Other> *, | |
| const volatile storage_1 *>::value> | |
| construct (Other &&other) | |
| { | |
| if (other.m_has_value) | |
| this->construct_value (std::forward<Other> (other).value ()); | |
| else | |
| this->construct_unex (std::forward<Other> (other).unex ()); | |
| } | |
| // reinit-expected exposition-only helper. | |
| // Pick branch of reinit-expected helper. | |
| template<typename T2, typename U, typename... Args> | |
| static constexpr int | |
| pick_reinit_expected () | |
| { | |
| if (std::is_same<U, void_storage>::value) | |
| // We're implementing expected<[cv] void, E> by reusing the storage | |
| // for expected<T, E> but giving it a dummy empty struct. | |
| // | |
| // However, doing this naively runs into a problem: if E cannot be | |
| // nothrow-constructed but can be nothrow-moved, reinit_expected will | |
| // construct a temporary E and move it into place, rather than | |
| // constructing it directly as [expected.void.assign] says. | |
| // | |
| // You may think we can just use branch 1 always then, but that | |
| // leaves us with a destroyed m_value and no m_unex, and with | |
| // m_has_value = true, so accesses down the line can trigger issues. | |
| // | |
| // In reality, we don't use exceptions in GCC. So, whatever, we can | |
| // take the higher-overhead branch. | |
| return 3; | |
| else if (std::is_nothrow_constructible<T2, Args...>::value) | |
| return 1; | |
| else if (std::is_nothrow_move_constructible<T2>::value) | |
| return 2; | |
| else | |
| return 3; | |
| } | |
| template<typename T2, typename U, typename... Args> | |
| GCC_BACKPORT20_CONSTEXPR void | |
| reinit_expected_ (std::integral_constant<int, 1>, | |
| T2& newval, U& oldval, Args&&... args) | |
| { | |
| gcc::destroy_at (std::addressof (oldval)); | |
| gcc::construct_at (std::addressof (newval), | |
| std::forward<Args> (args)...); | |
| } | |
| template<typename T2, typename U, typename... Args> | |
| GCC_BACKPORT20_CONSTEXPR void | |
| reinit_expected_ (std::integral_constant<int, 2>, | |
| T2& newval, U& oldval, Args&&... args) | |
| { | |
| T2 tmp (std::forward<Args> (args)...); | |
| gcc::destroy_at (std::addressof (oldval)); | |
| gcc::construct_at (std::addressof (newval), std::move (tmp)); | |
| } | |
| template<typename T2, typename U, typename... Args> | |
| GCC_BACKPORT20_CONSTEXPR void | |
| reinit_expected_ (std::integral_constant<int, 3>, | |
| T2& newval, U& oldval, Args&&... args) | |
| { | |
| U tmp (std::move (oldval)); | |
| gcc::destroy_at (std::addressof (oldval)); | |
| g_try | |
| { | |
| gcc::construct_at (std::addressof (newval), | |
| std::forward<Args> (args)...); | |
| } | |
| g_catch (...) | |
| { | |
| gcc::construct_at (std::addressof (oldval), | |
| std::move (tmp)); | |
| g_throw_exception_again; | |
| } | |
| } | |
| template<typename T2, typename U, typename... Args> | |
| GCC_BACKPORT20_CONSTEXPR void | |
| reinit_expected (T2& newval, U& oldval, Args&&... args) | |
| { | |
| constexpr int branch = pick_reinit_expected<T2, U, Args...>(); | |
| reinit_expected_ (std::integral_constant<int, branch> (), | |
| newval, oldval, std::forward<Args> (args)...); | |
| } | |
| // Assign m_value. | |
| template<typename U> | |
| GCC_BACKPORT20_CONSTEXPR void | |
| assign_value (U &&v) | |
| { | |
| if (this->m_has_value) | |
| // "If has_value() is true, equivalent to: val = std::forward<U>(v);" | |
| this->m_value = std::forward<U> (v); | |
| else | |
| // "Otherwise, equivalent to: reinit-expected(val, unex, | |
| // std::forward<U>(v)); has_val = true;" | |
| { | |
| reinit_expected (this->m_value, this->m_unex, std::forward<U> (v)); | |
| this->m_has_value = true; | |
| } | |
| } | |
| // Assign m_unex. | |
| template<typename Other> | |
| GCC_BACKPORT20_CONSTEXPR void | |
| assign_unex (Other &&other) | |
| { | |
| if (this->m_has_value) | |
| // "If has_value() is true, equivalent to: reinit-expected (unex, val, | |
| // std::forward<GF>(e.error())); has_val = false;" | |
| { | |
| // Must clear value first. | |
| reinit_expected (this->m_unex, this->m_value, | |
| std::forward<Other> (other)); | |
| this->m_has_value = false; | |
| } | |
| else | |
| // "Otherwise, equivalent to: unex = std::forward<GF>(e.error());" | |
| this->m_unex = std::forward<Other> (other); | |
| // Note that the std::forward<GF> (e.error ()) bit is hoisted to the | |
| // caller. | |
| } | |
| // Non-trivial move/copy assignment implementation. OTHER must be a | |
| // storage_1. Called by the respective special members. | |
| template<typename Other> | |
| GCC_BACKPORT20_CONSTEXPR | |
| std::enable_if_t<std::is_convertible<std::remove_reference_t<Other> *, | |
| const volatile storage_1 *>::value> | |
| assign (Other &&other) | |
| { | |
| if (this->m_has_value && other.m_has_value) | |
| // "If this->has_value() && rhs.has_value() is true, equivalent to | |
| // val = *rhs." / "If this->has_value() && rhs.has_value() is true, | |
| // equivalent to val = std::move(*rhs)." | |
| this->m_value = std::forward<Other> (other).value (); | |
| else if (this->m_has_value) | |
| // "Otherwise, if this->has_value() is true, equivalent to: | |
| // reinit-expected(unex, val, rhs.error())" / "Otherwise, if | |
| // this->has_value() is true, equivalent to: reinit-expected(unex, | |
| // val, std::move(rhs.error()))" | |
| reinit_expected (this->m_unex, this->m_value, | |
| std::forward<Other> (other).unex ()); | |
| else if (other.m_has_value) | |
| // "Otherwise, if rhs.has_value() is true, equivalent to: | |
| // reinit-expected(val, unex, *rhs)" / "Otherwise, if rhs.has_value() | |
| // is true, equivalent to: reinit-expected(val, unex, | |
| // std::move(*rhs))" | |
| reinit_expected (this->m_value, this->m_unex, | |
| std::forward<Other> (other).value ()); | |
| else | |
| // "Otherwise, equivalent to unex = std::move(rhs.error())." | |
| this->m_unex = std::forward<Other> (other).unex (); | |
| // "Then, if no exception was thrown, equivalent to: has_val = | |
| // rhs.has_value(); return *this;" | |
| this->m_has_value = other.m_has_value; | |
| } | |
| // [expected.object.swap]p2 "For the case where rhs.has_value() is false | |
| // and this->has_value() is true", without the final 'has_val' | |
| // reassignments. | |
| template<typename E_ = E> | |
| GCC_BACKPORT20_CONSTEXPR | |
| std::enable_if_t<std::is_nothrow_move_constructible<E_>::value> | |
| swap_1 (T &val, E &unex, | |
| T &rhs_val, E &rhs_unex) | |
| { | |
| E tmp (std::move (rhs_unex)); | |
| gcc::destroy_at (std::addressof (rhs_unex)); | |
| g_try | |
| { | |
| gcc::construct_at (std::addressof (rhs_val), std::move (val)); | |
| gcc::destroy_at (std::addressof (val)); | |
| gcc::construct_at (std::addressof (unex), std::move (tmp)); | |
| } | |
| g_catch (...) | |
| { | |
| gcc::construct_at (std::addressof (rhs_unex), std::move (tmp)); | |
| g_throw_exception_again; | |
| } | |
| } | |
| template<typename E_ = E> | |
| GCC_BACKPORT20_CONSTEXPR | |
| std::enable_if_t<!std::is_nothrow_move_constructible<E_>::value> | |
| swap_1 (T &val, E &unex, | |
| T &rhs_val, E &rhs_unex) | |
| { | |
| static_assert (std::is_nothrow_move_constructible<T>::value, | |
| "Can't swap without a nothrow-move-constructible T or E" | |
| ", is a constraint missing on swap?"); | |
| T tmp (std::move (val)); | |
| gcc::destroy_at (std::addressof (val)); | |
| g_try | |
| { | |
| gcc::construct_at (std::addressof (unex), std::move (rhs_unex)); | |
| gcc::destroy_at (std::addressof (rhs_unex)); | |
| gcc::construct_at (std::addressof (rhs_val), std::move (tmp)); | |
| } | |
| g_catch (...) | |
| { | |
| gcc::construct_at (std::addressof (val), std::move (tmp)); | |
| g_throw_exception_again; | |
| } | |
| } | |
| GCC_BACKPORT20_CONSTEXPR void | |
| _swap (storage_1 &other) | |
| { | |
| using std::swap; | |
| if (this->m_has_value && other.m_has_value) | |
| swap (this->m_value, other.m_value); | |
| else if (!this->m_has_value && !other.m_has_value) | |
| swap (this->m_unex, other.m_unex); | |
| else if (!this->m_has_value && other.m_has_value) | |
| other._swap (*this); | |
| else | |
| { | |
| swap_1 (this->m_value, this->m_unex, other.m_value, other.m_unex); | |
| this->m_has_value = false; | |
| other.m_has_value = true; | |
| } | |
| } | |
| }; | |
| // Copy operations. | |
| template<typename T, typename E, | |
| bool is_trivial = trait_2<std::is_trivially_copy_constructible, | |
| T, E>::value> | |
| struct storage_copy_cons; | |
| template<typename T, typename E> | |
| struct storage_copy_cons<T, E, true> : storage_1<T, E> | |
| { | |
| using storage_1<T, E>::storage_1; | |
| storage_copy_cons (storage_copy_cons &&) = default; | |
| storage_copy_cons & | |
| operator= (const storage_copy_cons& other) = default; | |
| storage_copy_cons & | |
| operator= (storage_copy_cons&& other) = default; | |
| // Does not require explicit copy constructor. | |
| storage_copy_cons (const storage_copy_cons &) = default; | |
| }; | |
| template<typename T, typename E> | |
| struct storage_copy_cons<T, E, false> : storage_1<T, E> | |
| { | |
| using storage_1<T, E>::storage_1; | |
| storage_copy_cons (storage_copy_cons &&) = default; | |
| storage_copy_cons & | |
| operator= (const storage_copy_cons& other) = default; | |
| storage_copy_cons & | |
| operator= (storage_copy_cons&& other) = default; | |
| // Requires explicit copy constructor. | |
| constexpr | |
| storage_copy_cons (const storage_copy_cons &o) | |
| : storage_1<T, E>(in_unused) | |
| { this->construct (o); } | |
| }; | |
| template<typename T, typename E, | |
| bool is_trivial = and_< | |
| std::is_trivially_copy_constructible<T>, | |
| std::is_trivially_copy_assignable<T>, | |
| std::is_trivially_destructible<T>, | |
| std::is_trivially_copy_constructible<E>, | |
| std::is_trivially_copy_assignable<E>, | |
| std::is_trivially_destructible<E> | |
| >::value> | |
| struct storage_copy_assign; | |
| template<typename T, typename E> | |
| struct storage_copy_assign<T, E, true> : storage_copy_cons<T, E> | |
| { | |
| using storage_copy_cons<T, E>::storage_copy_cons; | |
| storage_copy_assign (const storage_copy_assign &) = default; | |
| storage_copy_assign (storage_copy_assign &&) = default; | |
| storage_copy_assign & | |
| operator= (storage_copy_assign&& other) = default; | |
| // Trivially copy-assignable. | |
| storage_copy_assign & | |
| operator= (const storage_copy_assign& other) | |
| noexcept (and_<trait_2<std::is_nothrow_copy_assignable, T, E>, | |
| trait_2<std::is_nothrow_copy_constructible, T, E>>::value) | |
| = default; | |
| }; | |
| template<typename T, typename E> | |
| struct storage_copy_assign<T, E, false> : storage_copy_cons<T, E> | |
| { | |
| using storage_copy_cons<T, E>::storage_copy_cons; | |
| storage_copy_assign (const storage_copy_assign &) = default; | |
| storage_copy_assign (storage_copy_assign &&) = default; | |
| storage_copy_assign & | |
| operator= (storage_copy_assign&& other) = default; | |
| // Requires explicit copy assignment. | |
| GCC_BACKPORT20_CONSTEXPR storage_copy_assign & | |
| operator= (const storage_copy_assign& other) | |
| noexcept (and_<trait_2<std::is_nothrow_copy_assignable, T, E>, | |
| trait_2<std::is_nothrow_copy_constructible, T, E>>::value) | |
| { | |
| this->assign (other); | |
| return *this; | |
| } | |
| }; | |
| // Move operations. | |
| template<typename T, typename E, | |
| bool is_trivial = trait_2<std::is_trivially_move_constructible, | |
| T, E>::value> | |
| struct storage_move_cons; | |
| template<typename T, typename E> | |
| struct storage_move_cons<T, E, true> : storage_copy_assign<T, E> | |
| { | |
| using storage_copy_assign<T, E>::storage_copy_assign; | |
| storage_move_cons (const storage_move_cons &) = default; | |
| storage_move_cons & | |
| operator= (const storage_move_cons &) = default; | |
| storage_move_cons & | |
| operator= (storage_move_cons &&) = default; | |
| // Does not require explicit move constructor. | |
| storage_move_cons (storage_move_cons &&) = default; | |
| }; | |
| template<typename T, typename E> | |
| struct storage_move_cons<T, E, false> : storage_copy_assign<T, E> | |
| { | |
| using storage_copy_assign<T, E>::storage_copy_assign; | |
| storage_move_cons (const storage_move_cons &) = default; | |
| storage_move_cons &operator= (const storage_move_cons &) = default; | |
| storage_move_cons &operator= (storage_move_cons &&) = default; | |
| // Requires explicit move constructor. | |
| constexpr | |
| storage_move_cons (storage_move_cons &&o) | |
| noexcept (trait_2<std::is_nothrow_move_constructible, T, E>::value) | |
| : storage_copy_assign<T, E>(in_unused) | |
| { this->construct (std::move (o)); } | |
| }; | |
| template<typename T, typename E, | |
| bool is_trivial = and_< | |
| trait_2<std::is_trivially_move_constructible, T, E>, | |
| trait_2<std::is_trivially_move_assignable, T, E>, | |
| trait_2<std::is_trivially_destructible, T, E> | |
| >::value> | |
| struct storage_move_assign; | |
| template<typename T, typename E> | |
| struct storage_move_assign<T, E, true> : storage_move_cons<T, E> | |
| { | |
| using storage_move_cons<T, E>::storage_move_cons; | |
| storage_move_assign (storage_move_assign&&) = default; | |
| storage_move_assign (const storage_move_assign&) = default; | |
| storage_move_assign& | |
| operator= (const storage_move_assign&) = default; | |
| // Trivially move-assignable. | |
| storage_move_assign & | |
| operator= (storage_move_assign&& other) | |
| noexcept (and_<trait_2<std::is_nothrow_move_assignable, T, E>, | |
| trait_2<std::is_nothrow_move_constructible, T, E>>::value) | |
| = default; | |
| }; | |
| template<typename T, typename E> | |
| struct storage_move_assign<T, E, false> : storage_move_cons<T, E> | |
| { | |
| using storage_move_cons<T, E>::storage_move_cons; | |
| storage_move_assign (storage_move_assign&&) = default; | |
| storage_move_assign (const storage_move_assign&) = default; | |
| storage_move_assign& | |
| operator= (const storage_move_assign&) = default; | |
| // Requires explicit move assignment. | |
| GCC_BACKPORT20_CONSTEXPR storage_move_assign & | |
| operator= (storage_move_assign&& other) | |
| noexcept (and_<trait_2<std::is_nothrow_move_assignable, T, E>, | |
| trait_2<std::is_nothrow_move_constructible, T, E>>::value) | |
| { | |
| this->assign (std::move (other)); | |
| return *this; | |
| } | |
| }; | |
| // Default construction. | |
| template<typename T, typename E, | |
| bool is_default_cons = std::is_default_constructible<T>::value> | |
| struct storage_default_cons; | |
| template<typename T, typename E> | |
| struct storage_default_cons<T, E, false> : storage_move_assign<T, E> | |
| { | |
| using storage_move_assign<T, E>::storage_move_assign; | |
| // No default constructor. | |
| constexpr storage_default_cons () = delete; | |
| }; | |
| template<typename T, typename E> | |
| struct storage_default_cons<T, E, true> : storage_move_assign<T, E> | |
| { | |
| using storage_move_assign<T, E>::storage_move_assign; | |
| constexpr storage_default_cons () | |
| noexcept (std::is_nothrow_constructible<T>::value) | |
| : storage_move_assign<T, E> (in_value) | |
| {} | |
| }; | |
| // Final link in the chain. | |
| template<typename T, typename E> | |
| using storage = storage_default_cons<T, E>; | |
| }} // namespace detail::exp | |
| // [expected.expected] | |
| template<typename T, typename E, bool is_void> | |
| struct [[nodiscard]] expected | |
| : private detail::exp::storage<T, E> | |
| , private G_Enable_copy_move< | |
| // Copy constructor. | |
| detail::exp::trait_2<std::is_copy_constructible, T, E>::value, | |
| // Copy assignment. | |
| and_<detail::exp::trait_2<std::is_copy_assignable, T, E>, | |
| detail::exp::trait_2<std::is_copy_constructible, T, E>, | |
| or_<std::is_nothrow_move_constructible<T>, | |
| std::is_nothrow_move_constructible<E>> | |
| >::value, | |
| // Move constructor. | |
| detail::exp::trait_2<std::is_move_constructible, T, E>::value, | |
| // Move assignment. | |
| and_<detail::exp::trait_2<std::is_move_assignable, T, E>, | |
| detail::exp::trait_2<std::is_move_constructible, T, E>, | |
| or_<std::is_nothrow_move_constructible<T>, | |
| std::is_nothrow_move_constructible<E>>>::value | |
| > | |
| { | |
| private: | |
| using storage = detail::exp::storage<T, E>; | |
| // If T is not cv bool, converts-from-any-cvref<T, expected<U, G>> and | |
| // is_constructible<unexpected<E>, cv expected<U, G> ref-qual> are false. | |
| template<typename G_Up, typename G_Gr, typename G_Unex = unexpected<E>, | |
| typename = std::remove_cv_t<T>> | |
| struct g_cons_from_expected | |
| : or_<std::is_constructible<T, expected<G_Up, G_Gr>&>, | |
| std::is_constructible<T, expected<G_Up, G_Gr>>, | |
| std::is_constructible<T, const expected<G_Up, G_Gr>&>, | |
| std::is_constructible<T, const expected<G_Up, G_Gr>>, | |
| std::is_convertible<expected<G_Up, G_Gr>&, T>, | |
| std::is_convertible<expected<G_Up, G_Gr>, T>, | |
| std::is_convertible<const expected<G_Up, G_Gr>&, T>, | |
| std::is_convertible<const expected<G_Up, G_Gr>, T>, | |
| std::is_constructible<G_Unex, expected<G_Up, G_Gr>&>, | |
| std::is_constructible<G_Unex, expected<G_Up, G_Gr>>, | |
| std::is_constructible<G_Unex, const expected<G_Up, G_Gr>&>, | |
| std::is_constructible<G_Unex, const expected<G_Up, G_Gr>> | |
| > | |
| {}; | |
| // _GLIBCXX_RESOLVE_LIB_DEFECTS | |
| // If t is cv bool, we know it can be constructed from expected<U, G>, | |
| // but we don't want to cause the expected (U&&) constructor to be used, | |
| // so we only check the is_constructible<unexpected<E>, ...> cases. | |
| template<typename G_Up, typename G_Gr, typename G_Unex> | |
| struct g_cons_from_expected<G_Up, G_Gr, G_Unex, bool> | |
| : or_<std::is_constructible<G_Unex, expected<G_Up, G_Gr>&>, | |
| std::is_constructible<G_Unex, expected<G_Up, G_Gr>>, | |
| std::is_constructible<G_Unex, const expected<G_Up, G_Gr>&>, | |
| std::is_constructible<G_Unex, const expected<G_Up, G_Gr>> | |
| > | |
| {}; | |
| template<typename G_Up, typename G_Gr> | |
| using g_explicit_conv | |
| = or_<not_<std::is_convertible<G_Up, T>>, | |
| not_<std::is_convertible<G_Gr, E>> | |
| >; | |
| // Constraints for 'swap' | |
| template<typename T_, typename E_> | |
| using Constrain_swap = std::enable_if_t<and_< | |
| g_is_swappable<T_>, | |
| g_is_swappable<E_>, | |
| std::is_move_constructible<T_>, | |
| std::is_move_constructible<E_>, | |
| or_< | |
| std::is_nothrow_move_constructible<T_>, | |
| std::is_nothrow_move_constructible<E_> | |
| > | |
| >::value>; | |
| public: | |
| static_assert (detail::exp::can_be_unexpected_val<E>::value, | |
| "E not a valid expected error type"); | |
| static_assert (detail::exp::can_be_expected_val<T>::value, | |
| "T cannot be expected<>"); | |
| using value_type = T; | |
| using error_type = E; | |
| using unexpected_type = unexpected<E>; | |
| template<typename U> | |
| using rebind = expected<U, error_type>; | |
| // [expected.object.obs] | |
| // [expected.object.obs]p1 | |
| [[nodiscard]] constexpr const T* | |
| operator-> () const noexcept | |
| { | |
| gccbackport_assert (this->m_has_value && "Accessing value of expected with no value"); | |
| return std::addressof (this->m_value); | |
| } | |
| [[nodiscard]] constexpr T* | |
| operator-> () noexcept | |
| { | |
| gccbackport_assert (this->m_has_value && "Accessing value of expected with no value"); | |
| return std::addressof (this->m_value); | |
| } | |
| // [expected.object.obs]p3 | |
| [[nodiscard]] constexpr const T& | |
| operator* () const & noexcept | |
| { | |
| gccbackport_assert (this->m_has_value && "Accessing value of expected with no value"); | |
| return this->m_value; | |
| } | |
| [[nodiscard]] constexpr T& | |
| operator* () & noexcept | |
| { | |
| gccbackport_assert (this->m_has_value && "Accessing value of expected with no value"); | |
| return this->m_value; | |
| } | |
| // [expected.object.obs]p5 | |
| [[nodiscard]] constexpr T&& | |
| operator* () && noexcept | |
| { | |
| gccbackport_assert (this->m_has_value && "Accessing value of expected with no value"); | |
| return std::move (this->m_value); | |
| } | |
| [[nodiscard]] constexpr const T&& | |
| operator* () const && noexcept | |
| { | |
| gccbackport_assert (this->m_has_value && "Accessing value of expected with no value"); | |
| return std::move (this->m_value); | |
| } | |
| // [expected.object.obs]p7 | |
| [[nodiscard]] constexpr explicit | |
| operator bool () const noexcept | |
| { return this->m_has_value; } | |
| [[nodiscard]] constexpr bool | |
| has_value () const noexcept | |
| { return this->m_has_value; } | |
| // [expected.object.obs]p8 | |
| [[nodiscard]] constexpr bool | |
| has_error () const noexcept | |
| { return !this->m_has_value; } | |
| // TODO(arsen): restore [[likely]] | |
| // [expected.object.obs]p9 | |
| constexpr const T& | |
| value () const & | |
| { | |
| static_assert (std::is_copy_constructible<E>::value, ""); | |
| if (this->m_has_value) | |
| return this->m_value; | |
| g_throw_or_abort (bad_expected_access<E> (gcc::as_const (error ()))); | |
| } | |
| constexpr T& | |
| value () & | |
| { | |
| static_assert (std::is_copy_constructible<E>::value, ""); | |
| if (this->m_has_value) | |
| return this->m_value; | |
| g_throw_or_abort (bad_expected_access<E> (gcc::as_const (error ()))); | |
| } | |
| // [expected.object.obs]p12 | |
| constexpr const T&& | |
| value () const && | |
| { | |
| static_assert (std::is_copy_constructible<E>::value, ""); | |
| static_assert (std::is_constructible<E, const E&&>::value, ""); | |
| if (this->m_has_value) | |
| return std::move (this->m_value); | |
| g_throw_or_abort (bad_expected_access<E> (std::move (error ()))); | |
| } | |
| constexpr T&& | |
| value () && | |
| { | |
| static_assert (std::is_copy_constructible<E>::value, ""); | |
| static_assert (std::is_constructible<E, E&&>::value, ""); | |
| if (this->m_has_value) | |
| return std::move (this->m_value); | |
| g_throw_or_abort (bad_expected_access<E> (std::move (error ()))); | |
| } | |
| // [expected.object.obs]p15 | |
| constexpr const E& | |
| error () const & noexcept | |
| { | |
| gccbackport_assert (!this->m_has_value && "Accessing error of expected with no error"); | |
| return this->m_unex; | |
| } | |
| constexpr E& | |
| error () & noexcept | |
| { | |
| gccbackport_assert (!this->m_has_value && "Accessing error of expected with no error"); | |
| return this->m_unex; | |
| } | |
| // [expected.object.obs]p17 | |
| constexpr const E&& | |
| error () const && noexcept | |
| { | |
| gccbackport_assert (!this->m_has_value && "Accessing error of expected with no error"); | |
| return std::move (this->m_unex); | |
| } | |
| constexpr E&& | |
| error () && noexcept | |
| { | |
| gccbackport_assert (!this->m_has_value && "Accessing error of expected with no error"); | |
| return std::move (this->m_unex); | |
| } | |
| // [expected.object.obs]p19 | |
| template<typename U = std::remove_cv_t<T>> | |
| constexpr T | |
| value_or (U &&v) const & | |
| { | |
| static_assert (std::is_convertible<U, T>::value, | |
| "lvalue-reference value_or requires that type of " | |
| "argument is convertible to the value type"); | |
| static_assert (std::is_copy_constructible<T>::value, | |
| "lvalue-reference value_or requires that value type is " | |
| "copy-constructible"); | |
| return (this->m_has_value | |
| ? **this | |
| : static_cast<T> (std::forward<U> (v))); | |
| } | |
| // [expected.object.obs]p21 | |
| template<typename U = std::remove_cv_t<T>> | |
| constexpr T | |
| value_or (U &&v) && | |
| { | |
| static_assert (std::is_convertible<U, T>::value, | |
| "rvalue-reference value_or requires that type of " | |
| "argument is convertible to the value type"); | |
| static_assert (std::is_move_constructible<T>::value, | |
| "rvalue-reference value_or requires that value type is " | |
| "move-constructible"); | |
| return (this->m_has_value | |
| ? std::move (**this) | |
| : static_cast<T> (std::forward<U> (v))); | |
| } | |
| // [expected.object.obs]p23 | |
| template<typename G = E> | |
| constexpr E | |
| error_or (G &&e) const & | |
| { | |
| static_assert (std::is_convertible<G, E>::value, | |
| "lvalue-reference error_or requires that type of " | |
| "argument is convertible to the value type"); | |
| static_assert (std::is_copy_constructible<E>::value, | |
| "lvalue-reference error_or requires that error type is " | |
| "copy-constructible"); | |
| if (this->m_has_value) | |
| return std::forward<G> (e); | |
| return this->m_unex; | |
| } | |
| // [expected.object.obs]p26 | |
| template<typename G = E> | |
| constexpr E | |
| error_or (G &&e) && | |
| { | |
| static_assert (std::is_convertible<G, E>::value, | |
| "rvalue-reference error_or requires that type of " | |
| "argument is convertible to the value type"); | |
| static_assert (std::is_move_constructible<E>::value, | |
| "rvalue-reference error_or requires that error type is " | |
| "move-constructible"); | |
| if (this->m_has_value) | |
| return std::forward<G> (e); | |
| return std::move (this->m_unex); | |
| } | |
| // Constructors [expected.object.cons] | |
| // [expected.object.cons]p1; condition provided by base class | |
| expected () = default; | |
| // [expected.object.cons]p6 and [expected.object.cons]p11 - the copy/move | |
| // constructors - are implemented by storage and constrained by | |
| // G_Enable_copy_move. | |
| expected (const expected&) = default; | |
| expected (expected&&) = default; | |
| // [expected.object.cons]p17 | |
| template<typename U, typename G, | |
| G_Requires< | |
| // Constraints: | |
| not_<g_cons_from_expected<U, G>>, | |
| std::is_constructible<T, const U&>, | |
| std::is_constructible<E, const G&>, | |
| // !Explicit: | |
| not_<g_explicit_conv<const U&, const G&>> | |
| > = false> | |
| constexpr expected (const expected<U, G>& rhs) | |
| : storage (detail::exp::in_unused) | |
| { | |
| if (rhs.has_value ()) | |
| this->construct_value (*rhs); | |
| else | |
| this->construct_unex (rhs.error ()); | |
| } | |
| template<typename U, typename G, | |
| G_Requires< | |
| // Constraints: | |
| not_<g_cons_from_expected<U, G>>, | |
| std::is_constructible<T, const U&>, | |
| std::is_constructible<E, const G&>, | |
| // Explicit: | |
| g_explicit_conv<const U&, const G&> | |
| > = false> | |
| constexpr explicit expected (const expected<U, G>& rhs) | |
| : storage (detail::exp::in_unused) | |
| { | |
| if (rhs.has_value ()) | |
| this->construct_value (*rhs); | |
| else | |
| this->construct_unex (rhs.error ()); | |
| } | |
| template<typename U, typename G, | |
| G_Requires< | |
| // Constraints: | |
| not_<g_cons_from_expected<U, G>>, | |
| std::is_constructible<T, U&&>, | |
| std::is_constructible<E, G&&>, | |
| // !Explicit: | |
| not_<g_explicit_conv<U, G>> | |
| > = false> | |
| constexpr expected (expected<U, G>&& rhs) | |
| : storage (detail::exp::in_unused) | |
| { | |
| if (rhs.has_value ()) | |
| this->construct_value (std::move (*rhs)); | |
| else | |
| this->construct_unex (std::move (rhs.m_unex)); | |
| } | |
| template<typename U, typename G, | |
| G_Requires< | |
| // Constraints: | |
| not_<g_cons_from_expected<U, G>>, | |
| std::is_constructible<T, U&&>, | |
| std::is_constructible<E, G&&>, | |
| // Explicit: | |
| g_explicit_conv<U, G> | |
| > = false> | |
| constexpr explicit expected (expected<U, G>&& rhs) | |
| : storage (detail::exp::in_unused) | |
| { | |
| if (rhs.has_value ()) | |
| this->construct_value (std::move (*rhs)); | |
| else | |
| this->construct_unex (std::move (rhs.m_unex)); | |
| } | |
| // [expected.object.cons]p23 | |
| template<typename U = std::remove_cv_t<T>, | |
| G_Requires< | |
| // Constraints: | |
| not_<std::is_same<remove_cvref_t<U>, in_place_t>>, | |
| not_<std::is_same<remove_cvref_t<U>, expected>>, | |
| not_<std::is_same<remove_cvref_t<U>, unexpect_t>>, | |
| not_<detail::exp::is_unexpected<remove_cvref_t<U>>>, | |
| std::is_constructible<T, U>, | |
| // "if T is cv bool, remove_cvref_t<U> is not a specialization | |
| // of expected" | |
| or_<not_<std::is_same<std::remove_cv_t<T>, bool>>, | |
| not_<detail::exp::is_expected<remove_cvref_t<U>>>>, | |
| // !Explicit: | |
| std::is_convertible<U, T> | |
| > = false> | |
| constexpr expected (U&& v) | |
| : storage (detail::exp::in_value, std::forward<U> (v)) | |
| {} | |
| template<typename U = std::remove_cv_t<T>, | |
| G_Requires< | |
| // Constraints: | |
| not_<std::is_same<remove_cvref_t<U>, in_place_t>>, | |
| not_<std::is_same<remove_cvref_t<U>, expected>>, | |
| not_<std::is_same<remove_cvref_t<U>, unexpect_t>>, | |
| not_<detail::exp::is_unexpected<remove_cvref_t<U>>>, | |
| std::is_constructible<T, U>, | |
| // "if T is cv bool, remove_cvref_t<U> is not a specialization | |
| // of expected" | |
| or_<not_<std::is_same<std::remove_cv_t<T>, bool>>, | |
| not_<detail::exp::is_expected<remove_cvref_t<U>>>>, | |
| // Explicit: | |
| not_<std::is_convertible<U, T>> | |
| > = false> | |
| constexpr explicit expected (U&& v) | |
| : storage (detail::exp::in_value, std::forward<U> (v)) | |
| {} | |
| // [expected.object.cons]p27 | |
| template<typename G, | |
| G_Requires< | |
| // Constraints: | |
| std::is_constructible<E, const G&>, | |
| // !Explicit: | |
| std::is_convertible<const G&, E> | |
| > = false> | |
| constexpr expected (const unexpected<G> &e) | |
| : storage (detail::exp::in_unex, std::forward<const G&> (e.error ())) | |
| {} | |
| template<typename G, | |
| G_Requires< | |
| // Constraints: | |
| std::is_constructible<E, const G&>, | |
| // Explicit: | |
| not_<std::is_convertible<const G&, E>> | |
| > = false> | |
| constexpr explicit expected (const unexpected<G> &e) | |
| : storage (detail::exp::in_unex, std::forward<const G&> (e.error ())) | |
| {} | |
| template<typename G, | |
| G_Requires< | |
| // Constraints: | |
| std::is_constructible<E, G&&>, | |
| // !Explicit: | |
| std::is_convertible<G&&, E> | |
| > = false> | |
| constexpr expected (unexpected<G> &&e) | |
| : storage (detail::exp::in_unex, std::forward<G&&> (e.error ())) | |
| {} | |
| template<typename G, | |
| G_Requires< | |
| // Constraints: | |
| std::is_constructible<E, G&&>, | |
| // Explicit: | |
| not_<std::is_convertible<G&&, E>> | |
| > = false> | |
| constexpr explicit expected (unexpected<G> &&e) | |
| : storage (detail::exp::in_unex, std::forward<G&&> (e.error ())) | |
| {} | |
| // [expected.object.cons]p32 | |
| template<typename... Args, | |
| G_Requires< | |
| std::is_constructible<T, Args...> | |
| > = false> | |
| constexpr explicit expected (in_place_t, Args&&... args) | |
| : storage (detail::exp::in_value, std::forward<Args> (args)...) | |
| {} | |
| // [expected.object.cons]p36 | |
| template<typename U, typename... Args, | |
| G_Requires< | |
| std::is_constructible<T, | |
| std::initializer_list<U>&, Args...> | |
| > = false> | |
| constexpr explicit expected (in_place_t, std::initializer_list<U> il, | |
| Args&&... args) | |
| : storage (detail::exp::in_value, il, std::forward<Args> (args)...) | |
| {} | |
| // [expected.object.cons]p40 | |
| template<typename... Args, | |
| G_Requires< | |
| std::is_constructible<E, Args...> | |
| > = false> | |
| constexpr explicit expected (unexpect_t, Args&&... args) | |
| : storage (detail::exp::in_unex, std::forward<Args> (args)...) | |
| {} | |
| // [expected.object.cons]p44 | |
| template<typename U, typename... Args, | |
| G_Requires< | |
| std::is_constructible<E, std::initializer_list<U>&, Args...> | |
| > = false> | |
| constexpr explicit expected (unexpect_t, std::initializer_list<U> il, | |
| Args&&... args) | |
| : storage (detail::exp::in_unex, il, std::forward<Args> (args)...) | |
| {} | |
| // [expected.object.dtor], implemented in storage | |
| ~expected () = default; | |
| // [expected.object.assign] | |
| // The move/copy assignment operators are in the base class, and | |
| // constrained by G_Enable_copy_move, much like the constructors. | |
| expected & | |
| operator= (const expected &) = default; | |
| expected & | |
| operator= (expected &&) = default; | |
| // [expected.object.assign]p11 | |
| template<typename U = std::remove_cv_t<T>, | |
| G_Requires< | |
| not_<std::is_same<expected, remove_cvref_t<U>>>, | |
| not_<detail::exp::is_unexpected<remove_cvref_t<U>>>, | |
| std::is_constructible<T, U>, | |
| std::is_assignable<T&, U>, | |
| or_<std::is_nothrow_constructible<T, U>, | |
| std::is_nothrow_move_constructible<T>, | |
| std::is_nothrow_move_constructible<E>> | |
| > = false> | |
| GCC_BACKPORT20_CONSTEXPR expected& operator= (U&& v) | |
| { | |
| this->assign_value (std::forward<U> (v)); | |
| return *this; | |
| } | |
| // [expected.object.assign]p14 | |
| template<typename G, | |
| G_Requires< | |
| std::is_constructible<E, const G &>, | |
| std::is_assignable<E&, const G &>, | |
| or_<std::is_nothrow_constructible<E, const G &>, | |
| std::is_nothrow_move_constructible<T>, | |
| std::is_nothrow_move_constructible<E>> | |
| > = false> | |
| GCC_BACKPORT20_CONSTEXPR expected & | |
| operator= (const unexpected<G>& e) | |
| { | |
| this->assign_unex (std::forward<const G &> (e.error ())); | |
| return *this; | |
| } | |
| template<typename G, | |
| G_Requires< | |
| std::is_constructible<E, G &&>, | |
| std::is_assignable<E&, G &&>, | |
| or_<std::is_nothrow_constructible<E, G &&>, | |
| std::is_nothrow_move_constructible<T>, | |
| std::is_nothrow_move_constructible<E>> | |
| > = false> | |
| GCC_BACKPORT20_CONSTEXPR expected & | |
| operator= (unexpected<G>&& e) | |
| { | |
| this->assign_unex (std::forward<G &&> (e.error ())); | |
| return *this; | |
| } | |
| // [expected.object.assign]p18 | |
| template<typename... Args, | |
| G_Requires< | |
| std::is_nothrow_constructible<T, Args...> | |
| > = false> | |
| constexpr T & | |
| emplace (Args&&... args) noexcept | |
| { | |
| this->clear_storage (); | |
| this->m_has_value = true; | |
| return *gcc::construct_at (std::addressof (this->m_value), | |
| std::forward<Args> (args)...); | |
| } | |
| // [expected.object.assign]p20 | |
| template<typename U, typename... Args, | |
| G_Requires< | |
| std::is_nothrow_constructible<T, | |
| std::initializer_list<U>&, | |
| Args...> | |
| > = false> | |
| constexpr T & | |
| emplace (std::initializer_list<U> il, Args&&... args) noexcept | |
| { | |
| this->clear_storage (); | |
| this->m_has_value = true; | |
| return *gcc::construct_at (std::addressof (this->m_value), | |
| il, std::forward<Args> (args)...); | |
| } | |
| // [expected.object.swap] | |
| template<typename T_ = T, typename E_ = E> | |
| constexpr Constrain_swap<T_, E_> | |
| swap (expected& rhs) | |
| noexcept (and_<std::is_nothrow_move_constructible<T>, | |
| g_is_nothrow_swappable<T>, | |
| std::is_nothrow_move_constructible<E>, | |
| g_is_nothrow_swappable<E>>::value) | |
| { this->_swap (static_cast<storage &> (rhs)); } | |
| template<typename T_ = T, typename E_ = E> | |
| friend constexpr | |
| Constrain_swap<T_, E_> | |
| swap (expected& x, expected& y) noexcept (noexcept (x.swap (y))) | |
| { x.swap (y); } | |
| // [expected.object.monadic] | |
| // [expected.object.monadic]p1 | |
| template<typename F, | |
| typename E_ = E, | |
| G_Requires< | |
| std::is_constructible<E_, E_ &> | |
| > = false> | |
| constexpr auto | |
| and_then (F &&f) & | |
| { | |
| using U = remove_cvref_t<g_invoke_result_t<F, decltype (**this)>>; | |
| static_assert (detail::exp::is_expected_with_error<U, E>::value, | |
| "the function passed to std::expected<T, E>::and_then " | |
| "must return a std::expected with same error type"); | |
| if (has_value ()) | |
| return gcc::invoke (std::forward<F> (f), **this); | |
| else | |
| return U (unexpect, this->m_unex); | |
| } | |
| template<typename F, | |
| typename E_ = E, | |
| G_Requires< | |
| std::is_constructible<E_, const E_ &> | |
| > = false> | |
| constexpr auto | |
| and_then (F &&f) const & | |
| { | |
| using U = remove_cvref_t<g_invoke_result_t<F, decltype (**this)>>; | |
| static_assert (detail::exp::is_expected_with_error<U, E>::value, | |
| "the function passed to std::expected<T, E>::and_then " | |
| "must return a std::expected with same error type"); | |
| if (has_value ()) | |
| return gcc::invoke (std::forward<F> (f), **this); | |
| else | |
| return U (unexpect, this->m_unex); | |
| } | |
| // [expected.object.monadic]p5 | |
| template<typename F, | |
| typename E_ = E, | |
| G_Requires< | |
| std::is_constructible<E_, E_ &&> | |
| > = false> | |
| constexpr auto | |
| and_then (F &&f) && | |
| { | |
| using U = remove_cvref_t<g_invoke_result_t<F, decltype (std::move (**this))>>; | |
| static_assert (detail::exp::is_expected_with_error<U, E>::value, | |
| "the function passed to std::expected<T, E>::and_then " | |
| "must return a std::expected with same error type"); | |
| if (has_value ()) | |
| return gcc::invoke (std::forward<F> (f), std::move (**this)); | |
| else | |
| return U (unexpect, std::move (this->m_unex)); | |
| } | |
| template<typename F, | |
| typename E_ = E, | |
| G_Requires< | |
| std::is_constructible<E_, const E_ &&> | |
| > = false> | |
| constexpr auto | |
| and_then (F &&f) const && | |
| { | |
| using U = remove_cvref_t<g_invoke_result_t<F, decltype (std::move (**this))>>; | |
| static_assert (detail::exp::is_expected_with_error<U, E>::value, | |
| "the function passed to std::expected<T, E>::and_then " | |
| "must return a std::expected with same error type"); | |
| if (has_value ()) | |
| return gcc::invoke (std::forward<F> (f), std::move (**this)); | |
| else | |
| return U (unexpect, std::move (this->m_unex)); | |
| } | |
| // [expected.object.monadic]p9 | |
| template<typename F, | |
| typename T_ = T, | |
| G_Requires< | |
| std::is_constructible<T_, T_ &> | |
| > = false> | |
| constexpr auto | |
| or_else (F &&f) & | |
| { | |
| using G = remove_cvref_t<g_invoke_result_t<F, decltype (error ())>>; | |
| static_assert (detail::exp::is_expected_with_value<G, T>::value, | |
| "the function passed to std::expected<T, E>::or_else " | |
| "must return a std::expected with same value type"); | |
| if (has_value ()) | |
| return G (in_place, **this); | |
| else | |
| return gcc::invoke (std::forward<F> (f), this->m_unex); | |
| } | |
| template<typename F, | |
| typename T_ = T, | |
| G_Requires< | |
| std::is_constructible<T_, const T_&> | |
| > = false> | |
| constexpr auto | |
| or_else (F &&f) const & | |
| { | |
| using G = remove_cvref_t<g_invoke_result_t<F, decltype (error ())>>; | |
| static_assert (detail::exp::is_expected_with_value<G, T>::value, | |
| "the function passed to std::expected<T, E>::or_else " | |
| "must return a std::expected with same value type"); | |
| if (has_value ()) | |
| return G (in_place, **this); | |
| else | |
| return gcc::invoke (std::forward<F> (f), this->m_unex); | |
| } | |
| // [expected.object.monadic]p13 | |
| template<typename F, | |
| typename T_ = T, | |
| G_Requires< | |
| std::is_constructible<T_, T_ &&> | |
| > = false> | |
| constexpr auto | |
| or_else (F &&f) && | |
| { | |
| using G = remove_cvref_t<g_invoke_result_t<F, decltype (std::move (error ()))>>; | |
| static_assert (detail::exp::is_expected_with_value<G, T>::value, | |
| "the function passed to std::expected<T, E>::or_else " | |
| "must return a std::expected with same value type"); | |
| if (has_value ()) | |
| return G (in_place, std::move (**this)); | |
| else | |
| return gcc::invoke (std::forward<F> (f), std::move (this->m_unex)); | |
| } | |
| template<typename F, | |
| typename T_ = T, | |
| G_Requires< | |
| std::is_constructible<T_, const T_ &&> | |
| > = false> | |
| constexpr auto | |
| or_else (F &&f) const && | |
| { | |
| using G = remove_cvref_t<g_invoke_result_t<F, decltype (std::move (error ()))>>; | |
| static_assert (detail::exp::is_expected_with_value<G, T>::value, | |
| "the function passed to std::expected<T, E>::or_else " | |
| "must return a std::expected with same value type"); | |
| if (has_value ()) | |
| return G (in_place, std::move (**this)); | |
| else | |
| return gcc::invoke (std::forward<F> (f), std::move (this->m_unex)); | |
| } | |
| // [expected.object.monadic]p17 | |
| template<typename F, | |
| typename E_ = E, | |
| G_Requires< | |
| std::is_constructible<E_, E_ &> | |
| > = false> | |
| constexpr auto | |
| transform (F &&f) & | |
| { | |
| using U = std::remove_cv_t<g_invoke_result_t<F, decltype (**this)>>; | |
| using Res = expected<U, E>; | |
| if (has_value ()) | |
| return Res (detail::exp::in_place_inv{}, [&] () { | |
| return gcc::invoke (std::forward<F> (f), **this); | |
| }); | |
| else | |
| return Res (unexpect, this->m_unex); | |
| } | |
| template<typename F, | |
| typename E_ = E, | |
| G_Requires< | |
| std::is_constructible<E_, const E_ &> | |
| > = false> | |
| constexpr auto | |
| transform (F &&f) const & | |
| { | |
| using U = std::remove_cv_t<g_invoke_result_t<F, decltype (**this)>>; | |
| using Res = expected<U, E>; | |
| if (has_value ()) | |
| return Res (detail::exp::in_place_inv{}, [&] () { | |
| return gcc::invoke (std::forward<F> (f), **this); | |
| }); | |
| else | |
| return Res (unexpect, this->m_unex); | |
| } | |
| // [expected.object.monadic]p21 | |
| template<typename F, | |
| typename E_ = E, | |
| G_Requires< | |
| std::is_constructible<E_, E_ &&> | |
| > = false> | |
| constexpr auto | |
| transform (F &&f) && | |
| { | |
| using U = std::remove_cv_t<g_invoke_result_t<F, decltype (std::move (**this))>>; | |
| using Res = expected<U, E>; | |
| if (has_value ()) | |
| return Res (detail::exp::in_place_inv{}, [&] () { | |
| return gcc::invoke (std::forward<F> (f), std::move (**this)); | |
| }); | |
| else | |
| return Res (unexpect, std::move (this->m_unex)); | |
| } | |
| template<typename F, | |
| typename E_ = E, | |
| G_Requires< | |
| std::is_constructible<E_, const E_ &&> | |
| > = false> | |
| constexpr auto | |
| transform (F &&f) const && | |
| { | |
| using U = std::remove_cv_t<g_invoke_result_t<F, decltype (std::move (**this))>>; | |
| using Res = expected<U, E>; | |
| if (has_value ()) | |
| return Res (detail::exp::in_place_inv{}, [&] () { | |
| return gcc::invoke (std::forward<F> (f), std::move (**this)); | |
| }); | |
| else | |
| return Res (unexpect, std::move (this->m_unex)); | |
| } | |
| // [expected.object.monadic]p25 | |
| template<typename F, | |
| typename T_ = T, | |
| G_Requires< | |
| std::is_constructible<T_, T_ &> | |
| > = false> | |
| constexpr auto | |
| transform_error (F &&f) & | |
| { | |
| using G = std::remove_cv_t<g_invoke_result_t<F, decltype (error ())>>; | |
| using Res = expected<T, G>; | |
| if (has_value ()) | |
| return Res (in_place, **this); | |
| else | |
| return Res (detail::exp::unexpect_inv{}, [&] () { | |
| return gcc::invoke (std::forward<F> (f), this->m_unex); | |
| }); | |
| } | |
| template<typename F, | |
| typename T_ = T, | |
| G_Requires< | |
| std::is_constructible<T_, const T_ &> | |
| > = false> | |
| constexpr auto | |
| transform_error (F &&f) const & | |
| { | |
| using G = std::remove_cv_t<g_invoke_result_t<F, decltype (error ())>>; | |
| using Res = expected<T, G>; | |
| if (has_value ()) | |
| return Res (in_place, **this); | |
| else | |
| return Res (detail::exp::unexpect_inv{}, [&] () { | |
| return gcc::invoke (std::forward<F> (f), this->m_unex); | |
| }); | |
| } | |
| // [expected.object.monadic]p29 | |
| template<typename F, | |
| typename T_ = T, | |
| G_Requires< | |
| std::is_constructible<T_, T_ &&> | |
| > = false> | |
| constexpr auto | |
| transform_error (F &&f) && | |
| { | |
| using G = std::remove_cv_t<g_invoke_result_t<F, decltype (std::move (error ()))>>; | |
| using Res = expected<T, G>; | |
| if (has_value ()) | |
| return Res (in_place, std::move (**this)); | |
| else | |
| return Res (detail::exp::unexpect_inv{}, [&] () { | |
| return gcc::invoke (std::forward<F> (f), std::move (this->m_unex)); | |
| }); | |
| } | |
| template<typename F, | |
| typename T_ = T, | |
| G_Requires< | |
| std::is_constructible<T_, const T_ &&> | |
| > = false> | |
| constexpr auto | |
| transform_error (F &&f) const && | |
| { | |
| using G = std::remove_cv_t<g_invoke_result_t<F, decltype (std::move (error ()))>>; | |
| using Res = expected<T, G>; | |
| if (has_value ()) | |
| return Res (in_place, std::move (**this)); | |
| else | |
| return Res (detail::exp::unexpect_inv{}, [&] () { | |
| return gcc::invoke (std::forward<F> (f), std::move (this->m_unex)); | |
| }); | |
| } | |
| // [expected.object.eq] | |
| template<typename T2, typename E2> | |
| [[nodiscard]] friend constexpr | |
| detail::exp::eql_2_return_t<T, T2, E, E2> | |
| operator== (const expected &x, const expected<T2, E2> &y) | |
| { | |
| if (x.m_has_value && y.m_has_value) | |
| return *x == *y; | |
| else if (!x.m_has_value && !y.m_has_value) | |
| return x.m_unex == y.m_unex; | |
| return false; | |
| } | |
| template<typename T2, | |
| G_Requires< | |
| not_<detail::exp::is_expected<T2>> | |
| > = false> | |
| [[nodiscard]] friend constexpr | |
| eql_return_t<T, T2> | |
| operator== (const expected &x, const T2 &y) | |
| { | |
| if (!x.m_has_value) | |
| return false; | |
| return *x == y; | |
| } | |
| template<typename E2> | |
| [[nodiscard]] friend constexpr | |
| eql_return_t<E, E2> | |
| operator== (const expected &x, const unexpected<E2> &y) | |
| { | |
| if (x.m_has_value) | |
| return false; | |
| return x.m_unex == y.unex; | |
| } | |
| private: | |
| template<typename Fn> | |
| explicit constexpr | |
| expected (detail::exp::in_place_inv tag, Fn&& fn) | |
| : storage (tag, std::forward<Fn> (fn)) | |
| {} | |
| template<typename Fn> | |
| explicit constexpr | |
| expected (detail::exp::unexpect_inv tag, Fn&& fn) | |
| : storage (tag, std::forward<Fn> (fn)) | |
| {} | |
| template<typename, typename, bool> | |
| friend struct expected; | |
| }; | |
| template<typename Void, typename E> | |
| struct [[nodiscard]] expected<Void, E, true> | |
| : private detail::exp::storage<detail::exp::void_storage, E> | |
| , private G_Enable_copy_move< | |
| // Copy constructor. | |
| std::is_copy_constructible<E>::value, | |
| // Copy assignment. | |
| and_<std::is_copy_assignable<E>, | |
| std::is_copy_constructible<E>>::value, | |
| // Move constructor. | |
| std::is_move_constructible<E>::value, | |
| // Move assignment. | |
| and_<std::is_move_assignable<E>, | |
| std::is_move_constructible<E>>::value | |
| > | |
| { | |
| private: | |
| using storage = detail::exp::storage<detail::exp::void_storage, E>; | |
| template<typename G_Up, typename G_Gr, typename G_Unex = unexpected<E>> | |
| struct g_cons_from_expected | |
| : or_<std::is_constructible<G_Unex, expected<G_Up, G_Gr>&>, | |
| std::is_constructible<G_Unex, expected<G_Up, G_Gr>>, | |
| std::is_constructible<G_Unex, const expected<G_Up, G_Gr>&>, | |
| std::is_constructible<G_Unex, const expected<G_Up, G_Gr>> | |
| > | |
| {}; | |
| template<typename E_> | |
| using Constrain_swap = std::enable_if_t<and_< | |
| g_is_swappable<E_>, | |
| std::is_move_constructible<E_> | |
| >::value>; | |
| public: | |
| static_assert (detail::exp::can_be_unexpected_val<E>::value, | |
| "E not a valid expected error type"); | |
| using value_type = Void; | |
| using error_type = E; | |
| using unexpected_type = unexpected<E>; | |
| template<typename U> | |
| using rebind = expected<U, error_type>; | |
| // [expected.void.obs]p1 | |
| [[nodiscard]] constexpr explicit | |
| operator bool () const noexcept | |
| { return this->m_has_value; } | |
| [[nodiscard]] constexpr bool | |
| has_value () const noexcept | |
| { return this->m_has_value; } | |
| // [expected.void.obs]p2 | |
| [[nodiscard]] constexpr bool | |
| has_error () const noexcept | |
| { return !this->m_has_value; } | |
| // [expected.void.obs]p3 | |
| constexpr void | |
| operator* () const noexcept | |
| { | |
| gccbackport_assert (this->m_has_value && "Accessing value of expected with no value"); | |
| } | |
| // [expected.void.obs]p4 | |
| constexpr void | |
| value () const & | |
| { | |
| static_assert (std::is_copy_constructible<E>::value, ""); | |
| if (!this->m_has_value) | |
| g_throw_or_abort (bad_expected_access<E> (error ())); | |
| } | |
| // [expected.void.obs]p6 | |
| constexpr void | |
| value () && | |
| { | |
| static_assert (std::is_copy_constructible<E>::value, ""); | |
| static_assert (std::is_constructible<E, E&&>::value, ""); | |
| if (!this->m_has_value) | |
| g_throw_or_abort (bad_expected_access<E> (std::move (error ()))); | |
| } | |
| // [expected.void.obs]p8 | |
| constexpr const E& | |
| error () const & noexcept | |
| { | |
| gccbackport_assert (!this->m_has_value && "Accessing error of expected with no error"); | |
| return this->m_unex; | |
| } | |
| constexpr E& | |
| error () & noexcept | |
| { | |
| gccbackport_assert (!this->m_has_value && "Accessing error of expected with no error"); | |
| return this->m_unex; | |
| } | |
| // [expected.void.obs]p10 | |
| constexpr const E&& | |
| error () const && noexcept | |
| { | |
| gccbackport_assert (!this->m_has_value && "Accessing error of expected with no error"); | |
| return std::move (this->m_unex); | |
| } | |
| constexpr E&& | |
| error () && noexcept | |
| { | |
| gccbackport_assert (!this->m_has_value && "Accessing error of expected with no error"); | |
| return std::move (this->m_unex); | |
| } | |
| // [expected.void.obs]p12 | |
| template<typename G = E> | |
| constexpr E | |
| error_or (G &&e) const & | |
| { | |
| static_assert (std::is_convertible<G, E>::value, | |
| "lvalue-reference error_or requires that type of " | |
| "argument is convertible to the error type"); | |
| static_assert (std::is_copy_constructible<E>::value, | |
| "lvalue-reference error_or requires that error type is " | |
| "copy-constructible"); | |
| if (this->m_has_value) | |
| return std::forward<G> (e); | |
| return this->m_unex; | |
| } | |
| // [expected.void.obs]p14 | |
| template<typename G = E> | |
| constexpr E | |
| error_or (G &&e) && | |
| { | |
| static_assert (std::is_convertible<G, E>::value, | |
| "rvalue-reference error_or requires that type of " | |
| "argument is convertible to the error type"); | |
| static_assert (std::is_move_constructible<E>::value, | |
| "rvalue-reference error_or requires that error type is " | |
| "move-constructible"); | |
| if (this->m_has_value) | |
| return std::forward<G> (e); | |
| return std::move (this->m_unex); | |
| } | |
| // Constructors [expected.void.cons] | |
| // [expected.void.cons]p1; condition provided by base class | |
| expected () = default; | |
| // [expected.void.cons]p2 and [expected.void.cons]p7 - the copy/move | |
| // constructors - are implemented by storage and constrained by | |
| // G_Enable_copy_move. | |
| expected (const expected&) = default; | |
| expected (expected&&) = default; | |
| // [expected.void.cons]p12 | |
| template<typename U, typename G, | |
| G_Requires< | |
| // Constraints: | |
| std::is_void<U>, | |
| std::is_constructible<E, const G&>, | |
| not_<g_cons_from_expected<U, G>>, | |
| // !Explicit: | |
| std::is_convertible<const G&, E> | |
| > = false> | |
| constexpr expected (const expected<U, G>& rhs) | |
| : storage (detail::exp::in_unused) | |
| { | |
| if (rhs.has_value ()) | |
| this->construct_value (); | |
| else | |
| this->construct_unex (rhs.error ()); | |
| } | |
| template<typename U, typename G, | |
| G_Requires< | |
| // Constraints: | |
| std::is_void<U>, | |
| std::is_constructible<E, const G&>, | |
| not_<g_cons_from_expected<U, G>>, | |
| // Explicit: | |
| not_<std::is_convertible<const G&, E>> | |
| > = false> | |
| constexpr explicit expected (const expected<U, G>& rhs) | |
| : storage (detail::exp::in_unused) | |
| { | |
| if (rhs.has_value ()) | |
| this->construct_value (); | |
| else | |
| this->construct_unex (rhs.error ()); | |
| } | |
| template<typename U, typename G, | |
| G_Requires< | |
| // Constraints: | |
| std::is_void<U>, | |
| std::is_constructible<E, G&&>, | |
| not_<g_cons_from_expected<U, G>>, | |
| // !Explicit: | |
| std::is_convertible<G, E> | |
| > = false> | |
| constexpr expected (expected<U, G>&& rhs) | |
| : storage (detail::exp::in_unused) | |
| { | |
| if (rhs.has_value ()) | |
| this->construct_value (); | |
| else | |
| this->construct_unex (std::move (rhs.m_unex)); | |
| } | |
| template<typename U, typename G, | |
| G_Requires< | |
| // Constraints: | |
| std::is_void<U>, | |
| std::is_constructible<E, G &&>, | |
| not_<g_cons_from_expected<U, G>>, | |
| // Explicit: | |
| not_<std::is_convertible<G, E>> | |
| > = false> | |
| constexpr explicit expected (expected<U, G>&& rhs) | |
| : storage (detail::exp::in_unused) | |
| { | |
| if (rhs.has_value ()) | |
| this->construct_value (); | |
| else | |
| this->construct_unex (std::move (rhs.m_unex)); | |
| } | |
| // [expected.void.cons]p17 | |
| template<typename G, | |
| G_Requires< | |
| // Constraints: | |
| std::is_constructible<E, const G&>, | |
| // !Explicit: | |
| std::is_convertible<const G&, E> | |
| > = false> | |
| constexpr expected (const unexpected<G> &e) | |
| : storage (detail::exp::in_unex, std::forward<const G&> (e.error ())) | |
| {} | |
| template<typename G, | |
| G_Requires< | |
| // Constraints: | |
| std::is_constructible<E, const G&>, | |
| // Explicit: | |
| not_<std::is_convertible<const G&, E>> | |
| > = false> | |
| constexpr explicit expected (const unexpected<G> &e) | |
| : storage (detail::exp::in_unex, std::forward<const G&> (e.error ())) | |
| {} | |
| template<typename G, | |
| G_Requires< | |
| // Constraints: | |
| std::is_constructible<E, G&&>, | |
| // !Explicit: | |
| std::is_convertible<G&&, E> | |
| > = false> | |
| constexpr expected (unexpected<G> &&e) | |
| : storage (detail::exp::in_unex, std::forward<G&&> (e.error ())) | |
| {} | |
| template<typename G, | |
| G_Requires< | |
| // Constraints: | |
| std::is_constructible<E, G&&>, | |
| // Explicit: | |
| not_<std::is_convertible<G&&, E>> | |
| > = false> | |
| constexpr explicit expected (unexpected<G> &&e) | |
| : storage (detail::exp::in_unex, std::forward<G&&> (e.error ())) | |
| {} | |
| // [expected.void.cons]p22 | |
| constexpr explicit expected (in_place_t) noexcept | |
| : storage (detail::exp::in_value) | |
| {} | |
| // [expected.void.cons]p23 | |
| template<typename... Args, | |
| G_Requires< | |
| std::is_constructible<E, Args...> | |
| > = false> | |
| constexpr explicit expected (unexpect_t, Args&&... args) | |
| : storage (detail::exp::in_unex, std::forward<Args> (args)...) | |
| {} | |
| // [expected.void.cons]p27 | |
| template<typename U, typename... Args, | |
| G_Requires< | |
| std::is_constructible<E, std::initializer_list<U>&, Args...> | |
| > = false> | |
| constexpr explicit expected (unexpect_t, std::initializer_list<U> il, | |
| Args&&... args) | |
| : storage (detail::exp::in_unex, il, std::forward<Args> (args)...) | |
| {} | |
| // [expected.void.dtor], implemented in storage | |
| ~expected () = default; | |
| // [expected.void.assign]p11, p5 | |
| // The move/copy assignment operators are in the base class, and | |
| // constrained by G_Enable_copy_move, much like the constructors. | |
| expected & | |
| operator= (const expected &) = default; | |
| expected & | |
| operator= (expected &&) = default; | |
| // [expected.void.assign]p10 | |
| template<typename G, | |
| G_Requires< | |
| std::is_constructible<E, const G &>, | |
| std::is_assignable<E&, const G &> | |
| > = false> | |
| GCC_BACKPORT20_CONSTEXPR expected & | |
| operator= (const unexpected<G>& e) | |
| { | |
| this->assign_unex (std::forward<const G &> (e.error ())); | |
| return *this; | |
| } | |
| template<typename G, | |
| G_Requires< | |
| std::is_constructible<E, G &&>, | |
| std::is_assignable<E&, G &&> | |
| > = false> | |
| GCC_BACKPORT20_CONSTEXPR expected & | |
| operator= (unexpected<G>&& e) | |
| { | |
| this->assign_unex (std::forward<G &&> (e.error ())); | |
| return *this; | |
| } | |
| // [expected.void.assign]p14 | |
| constexpr void | |
| emplace () noexcept | |
| { | |
| this->clear_storage (); | |
| this->construct_value (); | |
| } | |
| // [expected.void.swap] | |
| // Not using storage::swap as in the case where one expected has a value | |
| // and the other does not, it makes an extra unneeded move in order to be | |
| // able to shift around the T value. We don't have a T value here, but we | |
| // pretend we do for sake of storage. | |
| template<typename E_ = E> | |
| constexpr Constrain_swap<E_> | |
| swap (expected& other) | |
| noexcept (and_<std::is_nothrow_move_constructible<E>, | |
| g_is_nothrow_swappable<E>>::value) | |
| { | |
| using std::swap; | |
| if (this->m_has_value && other.m_has_value) | |
| ; | |
| else if (!this->m_has_value && !other.m_has_value) | |
| swap (this->m_unex, other.m_unex); | |
| else if (!this->m_has_value && other.m_has_value) | |
| other.swap (*this); | |
| else | |
| { | |
| gcc::construct_at (std::addressof (this->m_unex), | |
| std::move (other.m_unex)); | |
| gcc::destroy_at (std::addressof (other.m_unex)); | |
| this->m_has_value = false; | |
| other.construct_value (); | |
| } | |
| } | |
| template<typename E_ = E> | |
| friend constexpr | |
| Constrain_swap<E_> | |
| swap (expected& x, expected& y) noexcept (noexcept (x.swap (y))) | |
| { x.swap (y); } | |
| // [expected.void.monadic]p1 | |
| template<typename F, | |
| typename E_ = E, | |
| G_Requires< | |
| std::is_constructible<E_, E_ &> | |
| > = false> | |
| constexpr auto | |
| and_then (F &&f) & | |
| { | |
| using U = remove_cvref_t<g_invoke_result_t<F>>; | |
| static_assert (detail::exp::is_expected_with_error<U, E>::value, | |
| "the function passed to std::expected<T, E>::and_then " | |
| "must return a std::expected with same error type"); | |
| if (has_value ()) | |
| return gcc::invoke (std::forward<F> (f)); | |
| else | |
| return U (unexpect, this->m_unex); | |
| } | |
| template<typename F, | |
| typename E_ = E, | |
| G_Requires< | |
| std::is_constructible<E_, const E_ &> | |
| > = false> | |
| constexpr auto | |
| and_then (F &&f) const & | |
| { | |
| using U = remove_cvref_t<g_invoke_result_t<F>>; | |
| static_assert (detail::exp::is_expected_with_error<U, E>::value, | |
| "the function passed to std::expected<T, E>::and_then " | |
| "must return a std::expected with same error type"); | |
| if (has_value ()) | |
| return gcc::invoke (std::forward<F> (f)); | |
| else | |
| return U (unexpect, this->m_unex); | |
| } | |
| // [expected.void.monadic]p5 | |
| template<typename F, | |
| typename E_ = E, | |
| G_Requires< | |
| std::is_constructible<E_, E_ &&> | |
| > = false> | |
| constexpr auto | |
| and_then (F &&f) && | |
| { | |
| using U = remove_cvref_t<g_invoke_result_t<F>>; | |
| static_assert (detail::exp::is_expected_with_error<U, E>::value, | |
| "the function passed to std::expected<T, E>::and_then " | |
| "must return a std::expected with same error type"); | |
| if (has_value ()) | |
| return gcc::invoke (std::forward<F> (f)); | |
| else | |
| return U (unexpect, std::move (this->m_unex)); | |
| } | |
| template<typename F, | |
| typename E_ = E, | |
| G_Requires< | |
| std::is_constructible<E_, const E_ &&> | |
| > = false> | |
| constexpr auto | |
| and_then (F &&f) const && | |
| { | |
| using U = remove_cvref_t<g_invoke_result_t<F>>; | |
| static_assert (detail::exp::is_expected_with_error<U, E>::value, | |
| "the function passed to std::expected<T, E>::and_then " | |
| "must return a std::expected with same error type"); | |
| if (has_value ()) | |
| return gcc::invoke (std::forward<F> (f)); | |
| else | |
| return U (unexpect, std::move (this->m_unex)); | |
| } | |
| // [expected.void.monadic]p9 | |
| template<typename F> | |
| constexpr auto | |
| or_else (F &&f) & | |
| { | |
| using G = remove_cvref_t<g_invoke_result_t<F, decltype (error ())>>; | |
| static_assert (detail::exp::is_expected_with_value<G, Void>::value, | |
| "the function passed to std::expected<T, E>::or_else " | |
| "must return a std::expected with same value type"); | |
| if (has_value ()) | |
| return G (); | |
| else | |
| return gcc::invoke (std::forward<F> (f), this->m_unex); | |
| } | |
| template<typename F> | |
| constexpr auto | |
| or_else (F &&f) const & | |
| { | |
| using G = remove_cvref_t<g_invoke_result_t<F, decltype (error ())>>; | |
| static_assert (detail::exp::is_expected_with_value<G, Void>::value, | |
| "the function passed to std::expected<T, E>::or_else " | |
| "must return a std::expected with same value type"); | |
| if (has_value ()) | |
| return G (); | |
| else | |
| return gcc::invoke (std::forward<F> (f), this->m_unex); | |
| } | |
| // [expected.void.monadic]p12 | |
| template<typename F> | |
| constexpr auto | |
| or_else (F &&f) && | |
| { | |
| using G = remove_cvref_t<g_invoke_result_t<F, decltype (std::move (error ()))>>; | |
| static_assert (detail::exp::is_expected_with_value<G, Void>::value, | |
| "the function passed to std::expected<T, E>::or_else " | |
| "must return a std::expected with same value type"); | |
| if (has_value ()) | |
| return G (); | |
| else | |
| return gcc::invoke (std::forward<F> (f), std::move (this->m_unex)); | |
| } | |
| template<typename F> | |
| constexpr auto | |
| or_else (F &&f) const && | |
| { | |
| using G = remove_cvref_t<g_invoke_result_t<F, decltype (std::move (error ()))>>; | |
| static_assert (detail::exp::is_expected_with_value<G, Void>::value, | |
| "the function passed to std::expected<T, E>::or_else " | |
| "must return a std::expected with same value type"); | |
| if (has_value ()) | |
| return G (); | |
| else | |
| return gcc::invoke (std::forward<F> (f), std::move (this->m_unex)); | |
| } | |
| // [expected.void.monadic]p15 | |
| template<typename F, | |
| typename E_ = E, | |
| G_Requires< | |
| std::is_constructible<E_, E_ &> | |
| > = false> | |
| constexpr auto | |
| transform (F &&f) & | |
| { | |
| using U = std::remove_cv_t<g_invoke_result_t<F>>; | |
| using Res = expected<U, E>; | |
| if (has_value ()) | |
| return Res (detail::exp::in_place_inv{}, [&] () { | |
| return gcc::invoke (std::forward<F> (f)); | |
| }); | |
| else | |
| return Res (unexpect, this->m_unex); | |
| } | |
| template<typename F, | |
| typename E_ = E, | |
| G_Requires< | |
| std::is_constructible<E_, const E_ &> | |
| > = false> | |
| constexpr auto | |
| transform (F &&f) const & | |
| { | |
| using U = std::remove_cv_t<g_invoke_result_t<F>>; | |
| using Res = expected<U, E>; | |
| if (has_value ()) | |
| return Res (detail::exp::in_place_inv{}, [&] () { | |
| return gcc::invoke (std::forward<F> (f)); | |
| }); | |
| else | |
| return Res (unexpect, this->m_unex); | |
| } | |
| // [expected.void.monadic]p19 | |
| template<typename F, | |
| typename E_ = E, | |
| G_Requires< | |
| std::is_constructible<E_, E_ &&> | |
| > = false> | |
| constexpr auto | |
| transform (F &&f) && | |
| { | |
| using U = std::remove_cv_t<g_invoke_result_t<F>>; | |
| using Res = expected<U, E>; | |
| if (has_value ()) | |
| return Res (detail::exp::in_place_inv{}, [&] () { | |
| return gcc::invoke (std::forward<F> (f)); | |
| }); | |
| else | |
| return Res (unexpect, std::move (this->m_unex)); | |
| } | |
| template<typename F, | |
| typename E_ = E, | |
| G_Requires< | |
| std::is_constructible<E_, const E_ &&> | |
| > = false> | |
| constexpr auto | |
| transform (F &&f) const && | |
| { | |
| using U = std::remove_cv_t<g_invoke_result_t<F>>; | |
| using Res = expected<U, E>; | |
| if (has_value ()) | |
| return Res (detail::exp::in_place_inv{}, [&] () { | |
| return gcc::invoke (std::forward<F> (f)); | |
| }); | |
| else | |
| return Res (unexpect, std::move (this->m_unex)); | |
| } | |
| // [expected.void.monadic]p23 | |
| template<typename F> | |
| constexpr auto | |
| transform_error (F &&f) & | |
| { | |
| using G = std::remove_cv_t<g_invoke_result_t<F, decltype (error ())>>; | |
| using Res = expected<Void, G>; | |
| if (has_value ()) | |
| return Res (); | |
| else | |
| return Res (detail::exp::unexpect_inv{}, [&] () { | |
| return gcc::invoke (std::forward<F> (f), this->m_unex); | |
| }); | |
| } | |
| template<typename F> | |
| constexpr auto | |
| transform_error (F &&f) const & | |
| { | |
| using G = std::remove_cv_t<g_invoke_result_t<F, decltype (error ())>>; | |
| using Res = expected<Void, G>; | |
| if (has_value ()) | |
| return Res (); | |
| else | |
| return Res (detail::exp::unexpect_inv{}, [&] () { | |
| return gcc::invoke (std::forward<F> (f), this->m_unex); | |
| }); | |
| } | |
| // [expected.void.monadic]p26 | |
| template<typename F> | |
| constexpr auto | |
| transform_error (F &&f) && | |
| { | |
| using G = std::remove_cv_t<g_invoke_result_t<F, decltype (std::move (error ()))>>; | |
| using Res = expected<Void, G>; | |
| if (has_value ()) | |
| return Res (); | |
| else | |
| return Res (detail::exp::unexpect_inv{}, [&] () { | |
| return gcc::invoke (std::forward<F> (f), std::move (this->m_unex)); | |
| }); | |
| } | |
| template<typename F> | |
| constexpr auto | |
| transform_error (F &&f) const && | |
| { | |
| using G = std::remove_cv_t<g_invoke_result_t<F, decltype (std::move (error ()))>>; | |
| using Res = expected<Void, G>; | |
| if (has_value ()) | |
| return Res (); | |
| else | |
| return Res (detail::exp::unexpect_inv{}, [&] () { | |
| return gcc::invoke (std::forward<F> (f), std::move (this->m_unex)); | |
| }); | |
| } | |
| // [expected.void.eq] | |
| template<typename T2, typename E2, | |
| G_Requires<std::is_void<T2>> = false> | |
| [[nodiscard]] friend constexpr | |
| eql_return_t<E, E2> | |
| operator== (const expected &x, const expected<T2, E2> &y) | |
| { | |
| if (x.m_has_value && y.m_has_value) | |
| return true; | |
| else if (!x.m_has_value && !y.m_has_value) | |
| return x.m_unex == y.m_unex; | |
| return false; | |
| } | |
| template<typename E2> | |
| [[nodiscard]] friend constexpr | |
| eql_return_t<E, E2> | |
| operator== (const expected &x, const unexpected<E2> &y) | |
| { | |
| if (x.m_has_value) | |
| return false; | |
| return x.m_unex == y.unex; | |
| } | |
| private: | |
| template<typename Fn> | |
| explicit constexpr | |
| expected (detail::exp::in_place_inv, Fn&& fn) | |
| : storage (detail::exp::in_value) | |
| { std::forward<Fn> (fn) (); } | |
| template<typename Fn> | |
| explicit constexpr | |
| expected (detail::exp::unexpect_inv tag, Fn&& fn) | |
| : storage (tag, std::forward<Fn> (fn)) | |
| {} | |
| template<typename, typename, bool> | |
| friend struct expected; | |
| }; | |
| // Inequality operators. | |
| template<typename T1, typename E1, typename T2, typename E2> | |
| [[nodiscard]] constexpr eql_return_t<expected<T1, E1>, expected<T2, E2>> | |
| operator!= (const expected<T1, E1>& x, const expected<T2, E2>& y) | |
| { return !(x == y); } | |
| template<typename T1, typename E1, typename T2> | |
| [[nodiscard]] constexpr eql_return_t<expected<T1, E1>, T2> | |
| operator!= (const expected<T1, E1>& x, const T2& y) | |
| { return !(x == y); } | |
| template<typename T1, typename E1, typename E2> | |
| [[nodiscard]] constexpr eql_return_t<expected<T1, E1>, unexpected<E2>> | |
| operator!= (const expected<T1, E1>& x, const unexpected<E2>& y) | |
| { return !(x == y); } | |
| template<typename E1, typename E2> | |
| [[nodiscard]] constexpr eql_return_t<unexpected<E1>, unexpected<E2>> | |
| operator!= (const unexpected<E1>& x, const unexpected<E2>& y) | |
| { | |
| return !(x == y); | |
| } | |
| } // namespace gcc | |
| #endif // GCC_EXPECTED |