| // <expected> -*- C++ -*- |
| |
| // Copyright The GNU Toolchain Authors |
| // |
| // This file is part of the GNU ISO C++ Library. This library 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. |
| |
| // This library 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. |
| |
| // Under Section 7 of GPL version 3, you are granted additional |
| // permissions described in the GCC Runtime Library Exception, version |
| // 3.1, as published by the Free Software Foundation. |
| |
| // You should have received a copy of the GNU General Public License and |
| // a copy of the GCC Runtime Library Exception along with this program; |
| // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see |
| // <http://www.gnu.org/licenses/>. |
| |
| /** @file include/expected |
| * This is a Standard C++ Library header. |
| */ |
| |
| #ifndef _GLIBCXX_EXPECTED |
| #define _GLIBCXX_EXPECTED |
| |
| #pragma GCC system_header |
| |
| #if __cplusplus > 202002L && __cpp_concepts >= 202002L |
| |
| #include <initializer_list> |
| #include <bits/exception.h> // exception |
| #include <bits/stl_construct.h> // construct_at |
| #include <bits/utility.h> // in_place_t |
| |
| namespace std _GLIBCXX_VISIBILITY(default) |
| { |
| _GLIBCXX_BEGIN_NAMESPACE_VERSION |
| |
| /** |
| * @defgroup expected_values Expected values |
| * @addtogroup utilities |
| * @since C++23 |
| * @{ |
| */ |
| |
| #define __cpp_lib_expected 202202L |
| |
| /// Discriminated union that holds an expected value or an error value. |
| /** |
| * @since C++23 |
| */ |
| template<typename _Tp, typename _Er> |
| class expected; |
| |
| /// Wrapper type used to pass an error value to a `std::expected`. |
| /** |
| * @since C++23 |
| */ |
| template<typename _Er> |
| class unexpected; |
| |
| /// Exception thrown by std::expected when the value() is not present. |
| /** |
| * @since C++23 |
| */ |
| template<typename _Er> |
| class bad_expected_access; |
| |
| template<> |
| class bad_expected_access<void> : public exception |
| { |
| protected: |
| bad_expected_access() noexcept { } |
| bad_expected_access(const bad_expected_access&) = default; |
| bad_expected_access(bad_expected_access&&) = default; |
| bad_expected_access& operator=(const bad_expected_access&) = default; |
| bad_expected_access& operator=(bad_expected_access&&) = default; |
| ~bad_expected_access() = default; |
| |
| public: |
| |
| [[nodiscard]] |
| const char* |
| what() const noexcept override |
| { return "bad access to std::expected without expected value"; } |
| }; |
| |
| template<typename _Er> |
| class bad_expected_access : public bad_expected_access<void> { |
| public: |
| explicit |
| bad_expected_access(_Er __e) : _M_val(std::move(__e)) { } |
| |
| // XXX const char* what() const noexcept override; |
| |
| [[nodiscard]] |
| _Er& |
| error() & noexcept |
| { return _M_val; } |
| |
| [[nodiscard]] |
| const _Er& |
| error() const & noexcept |
| { return _M_val; } |
| |
| [[nodiscard]] |
| _Er&& |
| error() && noexcept |
| { return std::move(_M_val); } |
| |
| [[nodiscard]] |
| const _Er&& |
| error() const && noexcept |
| { return std::move(_M_val); } |
| |
| private: |
| _Er _M_val; |
| }; |
| |
| /// Tag type for constructing unexpected values in a std::expected |
| /** |
| * @since C++23 |
| */ |
| struct unexpect_t |
| { |
| explicit unexpect_t() = default; |
| }; |
| |
| /// Tag for constructing unexpected values in a std::expected |
| /** |
| * @since C++23 |
| */ |
| inline constexpr unexpect_t unexpect{}; |
| |
| /// @cond undoc |
| namespace __expected |
| { |
| template<typename _Tp> |
| constexpr bool __is_expected = false; |
| template<typename _Tp, typename _Er> |
| constexpr bool __is_expected<expected<_Tp, _Er>> = true; |
| |
| template<typename _Tp> |
| constexpr bool __is_unexpected = false; |
| template<typename _Tp> |
| constexpr bool __is_unexpected<unexpected<_Tp>> = true; |
| |
| template<typename _Er> |
| concept __can_be_unexpected |
| = is_object_v<_Er> && (!is_array_v<_Er>) |
| && (!__expected::__is_unexpected<_Er>) |
| && (!is_const_v<_Er>) && (!is_volatile_v<_Er>); |
| } |
| /// @endcond |
| |
| template<typename _Er> |
| class unexpected |
| { |
| static_assert( __expected::__can_be_unexpected<_Er> ); |
| |
| public: |
| constexpr unexpected(const unexpected&) = default; |
| constexpr unexpected(unexpected&&) = default; |
| |
| template<typename _Err = _Er> |
| requires (!is_same_v<remove_cvref_t<_Err>, unexpected>) |
| && (!is_same_v<remove_cvref_t<_Err>, in_place_t>) |
| && is_constructible_v<_Er, _Err> |
| constexpr explicit |
| unexpected(_Err&& __e) |
| noexcept(is_nothrow_constructible_v<_Er, _Err>) |
| : _M_val(std::forward<_Err>(__e)) |
| { } |
| |
| template<typename... _Args> |
| requires is_constructible_v<_Er, _Args...> |
| constexpr explicit |
| unexpected(in_place_t, _Args&&... __args) |
| noexcept(is_nothrow_constructible_v<_Er, _Args...>) |
| : _M_val(std::forward<_Args>(__args)...) |
| { } |
| |
| template<typename _Up, typename... _Args> |
| requires is_constructible_v<_Er, initializer_list<_Up>&, _Args...> |
| constexpr explicit |
| unexpected(in_place_t, initializer_list<_Up> __il, _Args&&... __args) |
| noexcept(is_nothrow_constructible_v<_Er, initializer_list<_Up>&, |
| _Args...>) |
| : _M_val(__il, std::forward<_Args>(__args)...) |
| { } |
| |
| constexpr unexpected& operator=(const unexpected&) = default; |
| constexpr unexpected& operator=(unexpected&&) = default; |
| |
| |
| [[nodiscard]] |
| constexpr const _Er& |
| error() const & noexcept { return _M_val; } |
| |
| [[nodiscard]] |
| constexpr _Er& |
| error() & noexcept { return _M_val; } |
| |
| [[nodiscard]] |
| constexpr const _Er&& |
| error() const && noexcept { return std::move(_M_val); } |
| |
| [[nodiscard]] |
| constexpr _Er&& |
| error() && noexcept { return std::move(_M_val); } |
| |
| constexpr void |
| swap(unexpected& __other) noexcept(is_nothrow_swappable_v<_Er>) |
| { |
| static_assert( is_swappable_v<_Er> ); |
| using std::swap; |
| swap(_M_val, __other._M_val); |
| } |
| |
| template<typename _Err> |
| [[nodiscard]] |
| friend constexpr bool |
| operator==(const unexpected& __x, const unexpected<_Err>& __y) |
| { return __x._M_val == __y.error(); } |
| |
| friend constexpr void |
| swap(unexpected& __x, unexpected& __y) |
| noexcept(noexcept(__x.swap(__y))) |
| requires requires {__x.swap(__y);} |
| { __x.swap(__y); } |
| |
| private: |
| _Er _M_val; |
| }; |
| |
| template<typename _Er> unexpected(_Er) -> unexpected<_Er>; |
| |
| /// @cond undoc |
| namespace __expected |
| { |
| template<typename _Tp> |
| struct _Guard |
| { |
| static_assert( is_nothrow_move_constructible_v<_Tp> ); |
| |
| constexpr explicit |
| _Guard(_Tp& __x) |
| : _M_guarded(__builtin_addressof(__x)), _M_tmp(std::move(__x)) // nothrow |
| { std::destroy_at(_M_guarded); } |
| |
| constexpr |
| ~_Guard() |
| { |
| if (_M_guarded) [[unlikely]] |
| std::construct_at(_M_guarded, std::move(_M_tmp)); |
| } |
| |
| _Guard(const _Guard&) = delete; |
| _Guard& operator=(const _Guard&) = delete; |
| |
| constexpr _Tp&& |
| release() noexcept |
| { |
| _M_guarded = nullptr; |
| return std::move(_M_tmp); |
| } |
| |
| private: |
| _Tp* _M_guarded; |
| _Tp _M_tmp; |
| }; |
| |
| // reinit-expected helper from [expected.object.assign] |
| template<typename _Tp, typename _Up, typename _Vp> |
| constexpr void |
| __reinit(_Tp* __newval, _Up* __oldval, _Vp&& __arg) |
| noexcept(is_nothrow_constructible_v<_Tp, _Vp>) |
| { |
| if constexpr (is_nothrow_constructible_v<_Tp, _Vp>) |
| { |
| std::destroy_at(__oldval); |
| std::construct_at(__newval, std::forward<_Vp>(__arg)); |
| } |
| else if constexpr (is_nothrow_move_constructible_v<_Tp>) |
| { |
| _Tp __tmp(std::forward<_Vp>(__arg)); // might throw |
| std::destroy_at(__oldval); |
| std::construct_at(__newval, std::move(__tmp)); |
| } |
| else |
| { |
| _Guard<_Up> __guard(*__oldval); |
| std::construct_at(__newval, std::forward<_Vp>(__arg)); // might throw |
| __guard.release(); |
| } |
| } |
| } |
| /// @endcond |
| |
| template<typename _Tp, typename _Er> |
| class expected |
| { |
| static_assert( ! is_reference_v<_Tp> ); |
| static_assert( ! is_function_v<_Tp> ); |
| static_assert( ! is_same_v<remove_cv_t<_Tp>, in_place_t> ); |
| static_assert( ! is_same_v<remove_cv_t<_Tp>, unexpect_t> ); |
| static_assert( ! __expected::__is_unexpected<remove_cv_t<_Tp>> ); |
| static_assert( __expected::__can_be_unexpected<_Er> ); |
| |
| template<typename _Up, typename _Err, typename _Unex = unexpected<_Er>> |
| static constexpr bool __cons_from_expected |
| = __or_v<is_constructible<_Tp, expected<_Up, _Err>&>, |
| is_constructible<_Tp, expected<_Up, _Err>>, |
| is_constructible<_Tp, const expected<_Up, _Err>&>, |
| is_constructible<_Tp, const expected<_Up, _Err>>, |
| is_convertible<expected<_Up, _Err>&, _Tp>, |
| is_convertible<expected<_Up, _Err>, _Tp>, |
| is_convertible<const expected<_Up, _Err>&, _Tp>, |
| is_convertible<const expected<_Up, _Err>, _Tp>, |
| is_constructible<_Unex, expected<_Up, _Err>&>, |
| is_constructible<_Unex, expected<_Up, _Err>>, |
| is_constructible<_Unex, const expected<_Up, _Err>&>, |
| is_constructible<_Unex, const expected<_Up, _Err>> |
| >; |
| |
| template<typename _Up, typename _Err> |
| constexpr static bool __explicit_conv |
| = __or_v<__not_<is_convertible<_Up, _Tp>>, |
| __not_<is_convertible<_Err, _Er>> |
| >; |
| |
| public: |
| using value_type = _Tp; |
| using error_type = _Er; |
| using unexpected_type = unexpected<_Er>; |
| |
| template<typename _Up> |
| using rebind = expected<_Up, error_type>; |
| |
| constexpr |
| expected() |
| noexcept(is_nothrow_default_constructible_v<_Tp>) |
| requires is_default_constructible_v<_Tp> |
| : _M_val(), _M_has_value(true) |
| { } |
| |
| expected(const expected&) = default; |
| |
| constexpr |
| expected(const expected& __x) |
| noexcept(__and_v<is_nothrow_copy_constructible<_Tp>, |
| is_nothrow_copy_constructible<_Er>>) |
| requires is_copy_constructible_v<_Tp> && is_copy_constructible_v<_Er> |
| && (!is_trivially_copy_constructible_v<_Tp> |
| || !is_trivially_copy_constructible_v<_Er>) |
| : _M_invalid(), _M_has_value(__x._M_has_value) |
| { |
| if (_M_has_value) |
| std::construct_at(__builtin_addressof(_M_val), __x._M_val); |
| else |
| std::construct_at(__builtin_addressof(_M_unex), __x._M_unex); |
| } |
| |
| expected(expected&&) = default; |
| |
| constexpr |
| expected(expected&& __x) |
| noexcept(__and_v<is_nothrow_move_constructible<_Tp>, |
| is_nothrow_move_constructible<_Er>>) |
| requires is_move_constructible_v<_Tp> && is_move_constructible_v<_Er> |
| && (!is_trivially_move_constructible_v<_Tp> |
| || !is_trivially_move_constructible_v<_Er>) |
| : _M_invalid(), _M_has_value(__x._M_has_value) |
| { |
| if (_M_has_value) |
| std::construct_at(__builtin_addressof(_M_val), |
| std::move(__x)._M_val); |
| else |
| std::construct_at(__builtin_addressof(_M_unex), |
| std::move(__x)._M_unex); |
| } |
| |
| template<typename _Up, typename _Gr> |
| requires is_constructible_v<_Tp, const _Up&> |
| && is_constructible_v<_Er, const _Gr&> |
| && (!__cons_from_expected<_Up, _Gr>) |
| constexpr explicit(__explicit_conv<const _Up&, const _Gr&>) |
| expected(const expected<_Up, _Gr>& __x) |
| noexcept(__and_v<is_nothrow_constructible<_Tp, const _Up&>, |
| is_nothrow_constructible<_Er, const _Gr&>>) |
| : _M_invalid(), _M_has_value(__x._M_has_value) |
| { |
| if (_M_has_value) |
| std::construct_at(__builtin_addressof(_M_val), __x._M_val); |
| else |
| std::construct_at(__builtin_addressof(_M_unex), __x._M_unex); |
| } |
| |
| template<typename _Up, typename _Gr> |
| requires is_constructible_v<_Tp, _Up> |
| && is_constructible_v<_Er, _Gr> |
| && (!__cons_from_expected<_Up, _Gr>) |
| constexpr explicit(__explicit_conv<_Up, _Gr>) |
| expected(expected<_Up, _Gr>&& __x) |
| noexcept(__and_v<is_nothrow_constructible<_Tp, _Up>, |
| is_nothrow_constructible<_Er, _Gr>>) |
| : _M_invalid(), _M_has_value(__x._M_has_value) |
| { |
| if (_M_has_value) |
| std::construct_at(__builtin_addressof(_M_val), |
| std::move(__x)._M_val); |
| else |
| std::construct_at(__builtin_addressof(_M_unex), |
| std::move(__x)._M_unex); |
| } |
| |
| template<typename _Up = _Tp> |
| requires (!is_same_v<remove_cvref_t<_Up>, expected>) |
| && (!is_same_v<remove_cvref_t<_Up>, in_place_t>) |
| && (!__expected::__is_unexpected<remove_cvref_t<_Up>>) |
| && is_constructible_v<_Tp, _Up> |
| constexpr explicit(!is_convertible_v<_Up, _Tp>) |
| expected(_Up&& __v) |
| noexcept(is_nothrow_constructible_v<_Tp, _Up>) |
| : _M_val(std::forward<_Up>(__v)), _M_has_value(true) |
| { } |
| |
| template<typename _Gr = _Er> |
| requires is_constructible_v<_Er, const _Gr&> |
| constexpr explicit(!is_convertible_v<const _Gr&, _Er>) |
| expected(const unexpected<_Gr>& __u) |
| noexcept(is_nothrow_constructible_v<_Er, const _Gr&>) |
| : _M_unex(__u.error()), _M_has_value(false) |
| { } |
| |
| template<typename _Gr = _Er> |
| requires is_constructible_v<_Er, _Gr> |
| constexpr explicit(!is_convertible_v<_Gr, _Er>) |
| expected(unexpected<_Gr>&& __u) |
| noexcept(is_nothrow_constructible_v<_Er, _Gr>) |
| : _M_unex(std::move(__u).error()), _M_has_value(false) |
| { } |
| |
| template<typename... _Args> |
| requires is_constructible_v<_Tp, _Args...> |
| constexpr explicit |
| expected(in_place_t, _Args&&... __args) |
| noexcept(is_nothrow_constructible_v<_Tp, _Args...>) |
| : _M_val(std::forward<_Args>(__args)...), _M_has_value(true) |
| { } |
| |
| template<typename _Up, typename... _Args> |
| requires is_constructible_v<_Tp, initializer_list<_Up>&, _Args...> |
| constexpr explicit |
| expected(in_place_t, initializer_list<_Up> __il, _Args&&... __args) |
| noexcept(is_nothrow_constructible_v<_Tp, initializer_list<_Up>&, |
| _Args...>) |
| : _M_val(__il, std::forward<_Args>(__args)...), _M_has_value(true) |
| { } |
| |
| template<typename... _Args> |
| requires is_constructible_v<_Er, _Args...> |
| constexpr explicit |
| expected(unexpect_t, _Args&&... __args) |
| noexcept(is_nothrow_constructible_v<_Er, _Args...>) |
| : _M_unex(std::forward<_Args>(__args)...), _M_has_value(false) |
| { } |
| |
| template<typename _Up, typename... _Args> |
| requires is_constructible_v<_Er, initializer_list<_Up>&, _Args...> |
| constexpr explicit |
| expected(unexpect_t, initializer_list<_Up> __il, _Args&&... __args) |
| noexcept(is_nothrow_constructible_v<_Er, initializer_list<_Up>&, |
| _Args...>) |
| : _M_unex(__il, std::forward<_Args>(__args)...), _M_has_value(false) |
| { } |
| |
| constexpr ~expected() = default; |
| |
| constexpr ~expected() |
| requires (!is_trivially_destructible_v<_Tp>) |
| || (!is_trivially_destructible_v<_Er>) |
| { |
| if (_M_has_value) |
| std::destroy_at(__builtin_addressof(_M_val)); |
| else |
| std::destroy_at(__builtin_addressof(_M_unex)); |
| } |
| |
| // assignment |
| |
| expected& operator=(const expected&) = delete; |
| |
| constexpr expected& |
| operator=(const expected& __x) |
| noexcept(__and_v<is_nothrow_copy_constructible<_Tp>, |
| is_nothrow_copy_constructible<_Er>, |
| is_nothrow_copy_assignable<_Tp>, |
| is_nothrow_copy_assignable<_Er>>) |
| requires is_copy_assignable_v<_Tp> && is_copy_constructible_v<_Tp> |
| && is_copy_assignable_v<_Er> && is_copy_constructible_v<_Er> |
| && (is_nothrow_move_constructible_v<_Tp> |
| || is_nothrow_move_constructible_v<_Er>) |
| { |
| if (__x._M_has_value) |
| this->_M_assign_val(__x._M_val); |
| else |
| this->_M_assign_unex(__x._M_unex); |
| return *this; |
| } |
| |
| constexpr expected& |
| operator=(expected&& __x) |
| noexcept(__and_v<is_nothrow_move_constructible<_Tp>, |
| is_nothrow_move_constructible<_Er>, |
| is_nothrow_move_assignable<_Tp>, |
| is_nothrow_move_assignable<_Er>>) |
| requires is_move_assignable_v<_Tp> && is_move_constructible_v<_Tp> |
| && is_move_assignable_v<_Er> && is_move_constructible_v<_Er> |
| && (is_nothrow_move_constructible_v<_Tp> |
| || is_nothrow_move_constructible_v<_Er>) |
| { |
| if (__x._M_has_value) |
| _M_assign_val(std::move(__x._M_val)); |
| else |
| _M_assign_unex(std::move(__x._M_unex)); |
| return *this; |
| } |
| |
| template<typename _Up = _Tp> |
| requires (!is_same_v<expected, remove_cvref_t<_Up>>) |
| && (!__expected::__is_unexpected<remove_cvref_t<_Up>>) |
| && is_constructible_v<_Tp, _Up> && is_assignable_v<_Tp&, _Up> |
| && (is_nothrow_constructible_v<_Tp, _Up> |
| || is_nothrow_move_constructible_v<_Tp> |
| || is_nothrow_move_constructible_v<_Er>) |
| constexpr expected& |
| operator=(_Up&& __v) |
| { |
| _M_assign_val(std::forward<_Up>(__v)); |
| return *this; |
| } |
| |
| template<typename _Gr> |
| requires is_constructible_v<_Er, const _Gr&> |
| && is_assignable_v<_Er&, const _Gr&> |
| && (is_nothrow_constructible_v<_Er, const _Gr&> |
| || is_nothrow_move_constructible_v<_Tp> |
| || is_nothrow_move_constructible_v<_Er>) |
| constexpr expected& |
| operator=(const unexpected<_Gr>& __e) |
| { |
| _M_assign_unex(__e.error()); |
| return *this; |
| } |
| |
| template<typename _Gr> |
| requires is_constructible_v<_Er, _Gr> |
| && is_assignable_v<_Er&, _Gr> |
| && (is_nothrow_constructible_v<_Er, _Gr> |
| || is_nothrow_move_constructible_v<_Tp> |
| || is_nothrow_move_constructible_v<_Er>) |
| constexpr expected& |
| operator=(unexpected<_Gr>&& __e) |
| { |
| _M_assign_unex(std::move(__e).error()); |
| return *this; |
| } |
| |
| // modifiers |
| |
| template<typename... _Args> |
| requires is_nothrow_constructible_v<_Tp, _Args...> |
| constexpr _Tp& |
| emplace(_Args&&... __args) noexcept |
| { |
| if (_M_has_value) |
| std::destroy_at(__builtin_addressof(_M_val)); |
| else |
| { |
| std::destroy_at(__builtin_addressof(_M_unex)); |
| _M_has_value = true; |
| } |
| std::construct_at(__builtin_addressof(_M_val), |
| std::forward<_Args>(__args)...); |
| return _M_val; |
| } |
| |
| template<typename _Up, typename... _Args> |
| requires is_nothrow_constructible_v<_Tp, initializer_list<_Up>&, |
| _Args...> |
| constexpr _Tp& |
| emplace(initializer_list<_Up> __il, _Args&&... __args) noexcept |
| { |
| if (_M_has_value) |
| std::destroy_at(__builtin_addressof(_M_val)); |
| else |
| { |
| std::destroy_at(__builtin_addressof(_M_unex)); |
| _M_has_value = true; |
| } |
| std::construct_at(__builtin_addressof(_M_val), |
| __il, std::forward<_Args>(__args)...); |
| return _M_val; |
| } |
| |
| // swap |
| constexpr void |
| swap(expected& __x) |
| noexcept(__and_v<is_nothrow_move_constructible<_Tp>, |
| is_nothrow_move_constructible<_Er>, |
| is_nothrow_swappable<_Tp&>, |
| is_nothrow_swappable<_Er&>>) |
| requires is_swappable_v<_Tp> && is_swappable_v<_Er> |
| && is_move_constructible_v<_Tp> |
| && is_move_constructible_v<_Er> |
| && (is_nothrow_move_constructible_v<_Tp> |
| || is_nothrow_move_constructible_v<_Er>) |
| { |
| if (_M_has_value) |
| { |
| if (__x._M_has_value) |
| { |
| using std::swap; |
| swap(_M_val, __x._M_val); |
| } |
| else |
| this->_M_swap_val_unex(__x); |
| } |
| else |
| { |
| if (__x._M_has_value) |
| __x._M_swap_val_unex(*this); |
| else |
| { |
| using std::swap; |
| swap(_M_unex, __x._M_unex); |
| } |
| } |
| } |
| |
| // observers |
| |
| [[nodiscard]] |
| constexpr const _Tp* |
| operator->() const noexcept |
| { |
| __glibcxx_assert(_M_has_value); |
| return __builtin_addressof(_M_val); |
| } |
| |
| [[nodiscard]] |
| constexpr _Tp* |
| operator->() noexcept |
| { |
| __glibcxx_assert(_M_has_value); |
| return __builtin_addressof(_M_val); |
| } |
| |
| [[nodiscard]] |
| constexpr const _Tp& |
| operator*() const & noexcept |
| { |
| __glibcxx_assert(_M_has_value); |
| return _M_val; |
| } |
| |
| [[nodiscard]] |
| constexpr _Tp& |
| operator*() & noexcept |
| { |
| __glibcxx_assert(_M_has_value); |
| return _M_val; |
| } |
| |
| [[nodiscard]] |
| constexpr const _Tp&& |
| operator*() const && noexcept |
| { |
| __glibcxx_assert(_M_has_value); |
| return std::move(_M_val); |
| } |
| |
| [[nodiscard]] |
| constexpr _Tp&& |
| operator*() && noexcept |
| { |
| __glibcxx_assert(_M_has_value); |
| return std::move(_M_val); |
| } |
| |
| [[nodiscard]] |
| constexpr explicit |
| operator bool() const noexcept { return _M_has_value; } |
| |
| [[nodiscard]] |
| constexpr bool has_value() const noexcept { return _M_has_value; } |
| |
| constexpr const _Tp& |
| value() const & |
| { |
| if (_M_has_value) [[likely]] |
| return _M_val; |
| _GLIBCXX_THROW_OR_ABORT(bad_expected_access<_Er>(_M_unex)); |
| } |
| |
| constexpr _Tp& |
| value() & |
| { |
| if (_M_has_value) [[likely]] |
| return _M_val; |
| _GLIBCXX_THROW_OR_ABORT(bad_expected_access<_Er>(_M_unex)); |
| } |
| |
| constexpr const _Tp&& |
| value() const && |
| { |
| if (_M_has_value) [[likely]] |
| return std::move(_M_val); |
| _GLIBCXX_THROW_OR_ABORT(bad_expected_access<_Er>( |
| std::move(_M_unex))); |
| } |
| |
| constexpr _Tp&& |
| value() && |
| { |
| if (_M_has_value) [[likely]] |
| return std::move(_M_val); |
| _GLIBCXX_THROW_OR_ABORT(bad_expected_access<_Er>( |
| std::move(_M_unex))); |
| } |
| |
| constexpr const _Er& |
| error() const & noexcept |
| { |
| __glibcxx_assert(!_M_has_value); |
| return _M_unex; |
| } |
| |
| constexpr _Er& |
| error() & noexcept |
| { |
| __glibcxx_assert(!_M_has_value); |
| return _M_unex; |
| } |
| |
| constexpr const _Er&& |
| error() const && noexcept |
| { |
| __glibcxx_assert(!_M_has_value); |
| return std::move(_M_unex); |
| } |
| |
| constexpr _Er&& |
| error() && noexcept |
| { |
| __glibcxx_assert(!_M_has_value); |
| return std::move(_M_unex); |
| } |
| |
| template<typename _Up> |
| constexpr _Tp |
| value_or(_Up&& __v) const & |
| noexcept(__and_v<is_nothrow_copy_constructible<_Tp>, |
| is_nothrow_convertible<_Up, _Tp>>) |
| { |
| static_assert( is_copy_constructible_v<_Tp> ); |
| static_assert( is_convertible_v<_Up, _Tp> ); |
| |
| if (_M_has_value) |
| return _M_val; |
| return static_cast<_Tp>(std::forward<_Up>(__v)); |
| } |
| |
| template<typename _Up> |
| constexpr _Tp |
| value_or(_Up&& __v) && |
| noexcept(__and_v<is_nothrow_move_constructible<_Tp>, |
| is_nothrow_convertible<_Up, _Tp>>) |
| { |
| static_assert( is_move_constructible_v<_Tp> ); |
| static_assert( is_convertible_v<_Up, _Tp> ); |
| |
| if (_M_has_value) |
| return std::move(_M_val); |
| return static_cast<_Tp>(std::forward<_Up>(__v)); |
| } |
| |
| // equality operators |
| |
| template<typename _Up, typename _Er2> |
| requires (!is_void_v<_Up>) |
| friend constexpr bool |
| operator==(const expected& __x, const expected<_Up, _Er2>& __y) |
| noexcept(noexcept(bool(*__x == *__y)) |
| && noexcept(bool(__x.error() == __y.error()))) |
| { |
| if (__x.has_value()) |
| return __y.has_value() && bool(*__x == *__y); |
| else |
| return !__y.has_value() && bool(__x.error() == __y.error()); |
| } |
| |
| template<typename _Up> |
| friend constexpr bool |
| operator==(const expected& __x, const _Up& __v) |
| noexcept(noexcept(bool(*__x == __v))) |
| { return __x.has_value() && bool(*__x == __v); } |
| |
| template<typename _Er2> |
| friend constexpr bool |
| operator==(const expected& __x, const unexpected<_Er2>& __e) |
| noexcept(noexcept(bool(__x.error() == __e.error()))) |
| { return !__x.has_value() && bool(__x.error() == __e.error()); } |
| |
| friend constexpr void |
| swap(expected& __x, expected& __y) |
| noexcept(noexcept(__x.swap(__y))) |
| requires requires {__x.swap(__y);} |
| { __x.swap(__y); } |
| |
| private: |
| template<typename, typename> friend class expected; |
| |
| template<typename _Vp> |
| constexpr void |
| _M_assign_val(_Vp&& __v) |
| { |
| if (_M_has_value) |
| _M_val = std::forward<_Vp>(__v); |
| else |
| { |
| __expected::__reinit(__builtin_addressof(_M_val), |
| __builtin_addressof(_M_unex), |
| std::forward<_Vp>(__v)); |
| _M_has_value = true; |
| } |
| } |
| |
| template<typename _Vp> |
| constexpr void |
| _M_assign_unex(_Vp&& __v) |
| { |
| if (_M_has_value) |
| { |
| __expected::__reinit(__builtin_addressof(_M_unex), |
| __builtin_addressof(_M_val), |
| std::forward<_Vp>(__v)); |
| _M_has_value = false; |
| } |
| else |
| _M_unex = std::forward<_Vp>(__v); |
| } |
| |
| // Swap two expected objects when only one has a value. |
| // Precondition: this->_M_has_value && !__rhs._M_has_value |
| constexpr void |
| _M_swap_val_unex(expected& __rhs) |
| noexcept(__and_v<is_nothrow_move_constructible<_Er>, |
| is_nothrow_move_constructible<_Tp>>) |
| { |
| if constexpr (is_nothrow_move_constructible_v<_Er>) |
| { |
| __expected::_Guard<_Er> __guard(__rhs._M_unex); |
| std::construct_at(__builtin_addressof(__rhs._M_val), |
| std::move(_M_val)); // might throw |
| __rhs._M_has_value = true; |
| std::destroy_at(__builtin_addressof(_M_val)); |
| std::construct_at(__builtin_addressof(_M_unex), |
| __guard.release()); |
| _M_has_value = false; |
| } |
| else |
| { |
| __expected::_Guard<_Tp> __guard(__rhs._M_val); |
| std::construct_at(__builtin_addressof(_M_unex), |
| std::move(__rhs._M_unex)); // might throw |
| _M_has_value = false; |
| std::destroy_at(__builtin_addressof(__rhs._M_unex)); |
| std::construct_at(__builtin_addressof(__rhs._M_val), |
| __guard.release()); |
| __rhs._M_has_value = true; |
| } |
| } |
| |
| union { |
| struct { } _M_invalid; |
| _Tp _M_val; |
| _Er _M_unex; |
| }; |
| |
| bool _M_has_value; |
| }; |
| |
| // Partial specialization for std::expected<cv void, E> |
| template<typename _Tp, typename _Er> requires is_void_v<_Tp> |
| class expected<_Tp, _Er> |
| { |
| static_assert( __expected::__can_be_unexpected<_Er> ); |
| |
| template<typename _Up, typename _Err, typename _Unex = unexpected<_Er>> |
| static constexpr bool __cons_from_expected |
| = __or_v<is_constructible<_Unex, expected<_Up, _Err>&>, |
| is_constructible<_Unex, expected<_Up, _Err>>, |
| is_constructible<_Unex, const expected<_Up, _Err>&>, |
| is_constructible<_Unex, const expected<_Up, _Err>> |
| >; |
| |
| public: |
| using value_type = _Tp; |
| using error_type = _Er; |
| using unexpected_type = unexpected<_Er>; |
| |
| template<typename _Up> |
| using rebind = expected<_Up, error_type>; |
| |
| constexpr |
| expected() noexcept |
| : _M_void(), _M_has_value(true) |
| { } |
| |
| expected(const expected&) = default; |
| |
| constexpr |
| expected(const expected& __x) |
| noexcept(is_nothrow_copy_constructible_v<_Er>) |
| requires is_copy_constructible_v<_Er> |
| && (!is_trivially_copy_constructible_v<_Er>) |
| : _M_void(), _M_has_value(__x._M_has_value) |
| { |
| if (!_M_has_value) |
| std::construct_at(__builtin_addressof(_M_unex), __x._M_unex); |
| } |
| |
| expected(expected&&) = default; |
| |
| constexpr |
| expected(expected&& __x) |
| noexcept(is_nothrow_move_constructible_v<_Er>) |
| requires is_move_constructible_v<_Er> |
| && (!is_trivially_move_constructible_v<_Er>) |
| : _M_void(), _M_has_value(__x._M_has_value) |
| { |
| if (!_M_has_value) |
| std::construct_at(__builtin_addressof(_M_unex), |
| std::move(__x)._M_unex); |
| } |
| |
| template<typename _Up, typename _Gr> |
| requires is_void_v<_Up> |
| && is_constructible_v<_Er, const _Gr&> |
| && (!__cons_from_expected<_Up, _Gr>) |
| constexpr explicit(!is_convertible_v<const _Gr&, _Er>) |
| expected(const expected<_Up, _Gr>& __x) |
| noexcept(is_nothrow_constructible_v<_Er, const _Gr&>) |
| : _M_void(), _M_has_value(__x._M_has_value) |
| { |
| if (!_M_has_value) |
| std::construct_at(__builtin_addressof(_M_unex), __x._M_unex); |
| } |
| |
| template<typename _Up, typename _Gr> |
| requires is_void_v<_Up> |
| && is_constructible_v<_Er, _Gr> |
| && (!__cons_from_expected<_Up, _Gr>) |
| constexpr explicit(!is_convertible_v<_Gr, _Er>) |
| expected(expected<_Up, _Gr>&& __x) |
| noexcept(is_nothrow_constructible_v<_Er, _Gr>) |
| : _M_void(), _M_has_value(__x._M_has_value) |
| { |
| if (!_M_has_value) |
| std::construct_at(__builtin_addressof(_M_unex), |
| std::move(__x)._M_unex); |
| } |
| |
| template<typename _Gr = _Er> |
| requires is_constructible_v<_Er, const _Gr&> |
| constexpr explicit(!is_convertible_v<const _Gr&, _Er>) |
| expected(const unexpected<_Gr>& __u) |
| noexcept(is_nothrow_constructible_v<_Er, const _Gr&>) |
| : _M_unex(__u.error()), _M_has_value(false) |
| { } |
| |
| template<typename _Gr = _Er> |
| requires is_constructible_v<_Er, _Gr> |
| constexpr explicit(!is_convertible_v<_Gr, _Er>) |
| expected(unexpected<_Gr>&& __u) |
| noexcept(is_nothrow_constructible_v<_Er, _Gr>) |
| : _M_unex(std::move(__u).error()), _M_has_value(false) |
| { } |
| |
| template<typename... _Args> |
| constexpr explicit |
| expected(in_place_t) noexcept |
| : expected() |
| { } |
| |
| template<typename... _Args> |
| requires is_constructible_v<_Er, _Args...> |
| constexpr explicit |
| expected(unexpect_t, _Args&&... __args) |
| noexcept(is_nothrow_constructible_v<_Er, _Args...>) |
| : _M_unex(std::forward<_Args>(__args)...), _M_has_value(false) |
| { } |
| |
| template<typename _Up, typename... _Args> |
| requires is_constructible_v<_Er, initializer_list<_Up>&, _Args...> |
| constexpr explicit |
| expected(unexpect_t, initializer_list<_Up> __il, _Args&&... __args) |
| noexcept(is_nothrow_constructible_v<_Er, initializer_list<_Up>&, |
| _Args...>) |
| : _M_unex(__il, std::forward<_Args>(__args)...), _M_has_value(false) |
| { } |
| |
| constexpr ~expected() = default; |
| |
| constexpr ~expected() requires (!is_trivially_destructible_v<_Er>) |
| { |
| if (!_M_has_value) |
| std::destroy_at(__builtin_addressof(_M_unex)); |
| } |
| |
| // assignment |
| |
| expected& operator=(const expected&) = delete; |
| |
| constexpr expected& |
| operator=(const expected& __x) |
| noexcept(__and_v<is_nothrow_copy_constructible<_Er>, |
| is_nothrow_copy_assignable<_Er>>) |
| requires is_copy_constructible_v<_Er> |
| && is_copy_assignable_v<_Er> |
| { |
| if (__x._M_has_value) |
| emplace(); |
| else |
| _M_assign_unex(__x._M_unex); |
| return *this; |
| } |
| |
| constexpr expected& |
| operator=(expected&& __x) |
| noexcept(__and_v<is_nothrow_move_constructible<_Er>, |
| is_nothrow_move_assignable<_Er>>) |
| requires is_move_constructible_v<_Er> |
| && is_move_assignable_v<_Er> |
| { |
| if (__x._M_has_value) |
| emplace(); |
| else |
| _M_assign_unex(std::move(__x._M_unex)); |
| return *this; |
| } |
| |
| template<typename _Gr> |
| requires is_constructible_v<_Er, const _Gr&> |
| && is_assignable_v<_Er&, const _Gr&> |
| constexpr expected& |
| operator=(const unexpected<_Gr>& __e) |
| { |
| _M_assign_unex(__e.error()); |
| return *this; |
| } |
| |
| template<typename _Gr> |
| requires is_constructible_v<_Er, _Gr> |
| && is_assignable_v<_Er&, _Gr> |
| constexpr expected& |
| operator=(unexpected<_Gr>&& __e) |
| { |
| _M_assign_unex(std::move(__e.error())); |
| return *this; |
| } |
| |
| // modifiers |
| |
| constexpr void |
| emplace() noexcept |
| { |
| if (!_M_has_value) |
| { |
| std::destroy_at(__builtin_addressof(_M_unex)); |
| _M_has_value = true; |
| } |
| } |
| |
| // swap |
| constexpr void |
| swap(expected& __x) |
| noexcept(__and_v<is_nothrow_swappable<_Er&>, |
| is_nothrow_move_constructible<_Er>>) |
| requires is_swappable_v<_Er> && is_move_constructible_v<_Er> |
| { |
| if (_M_has_value) |
| { |
| if (!__x._M_has_value) |
| { |
| std::construct_at(__builtin_addressof(_M_unex), |
| std::move(__x._M_unex)); // might throw |
| std::destroy_at(__builtin_addressof(__x._M_unex)); |
| _M_has_value = false; |
| __x._M_has_value = true; |
| } |
| } |
| else |
| { |
| if (__x._M_has_value) |
| { |
| std::construct_at(__builtin_addressof(__x._M_unex), |
| std::move(_M_unex)); // might throw |
| std::destroy_at(__builtin_addressof(_M_unex)); |
| _M_has_value = true; |
| __x._M_has_value = false; |
| } |
| else |
| { |
| using std::swap; |
| swap(_M_unex, __x._M_unex); |
| } |
| } |
| } |
| |
| // observers |
| |
| [[nodiscard]] |
| constexpr explicit |
| operator bool() const noexcept { return _M_has_value; } |
| |
| [[nodiscard]] |
| constexpr bool has_value() const noexcept { return _M_has_value; } |
| |
| constexpr void |
| operator*() const noexcept { __glibcxx_assert(_M_has_value); } |
| |
| constexpr void |
| value() const& |
| { |
| if (_M_has_value) [[likely]] |
| return; |
| _GLIBCXX_THROW_OR_ABORT(bad_expected_access<_Er>(_M_unex)); |
| } |
| |
| constexpr void |
| value() && |
| { |
| if (_M_has_value) [[likely]] |
| return; |
| _GLIBCXX_THROW_OR_ABORT(bad_expected_access<_Er>(std::move(_M_unex))); |
| } |
| |
| constexpr const _Er& |
| error() const & noexcept |
| { |
| __glibcxx_assert(!_M_has_value); |
| return _M_unex; |
| } |
| |
| constexpr _Er& |
| error() & noexcept |
| { |
| __glibcxx_assert(!_M_has_value); |
| return _M_unex; |
| } |
| |
| constexpr const _Er&& |
| error() const && noexcept |
| { |
| __glibcxx_assert(!_M_has_value); |
| return std::move(_M_unex); |
| } |
| |
| constexpr _Er&& |
| error() && noexcept |
| { |
| __glibcxx_assert(!_M_has_value); |
| return std::move(_M_unex); |
| } |
| |
| // equality operators |
| |
| template<typename _Up, typename _Er2> |
| requires is_void_v<_Up> |
| friend constexpr bool |
| operator==(const expected& __x, const expected<_Up, _Er2>& __y) |
| noexcept(noexcept(bool(__x.error() == __y.error()))) |
| { |
| if (__x.has_value()) |
| return __y.has_value(); |
| else |
| return !__y.has_value() && bool(__x.error() == __y.error()); |
| } |
| |
| template<typename _Er2> |
| friend constexpr bool |
| operator==(const expected& __x, const unexpected<_Er2>& __e) |
| noexcept(noexcept(bool(__x.error() == __e.error()))) |
| { return !__x.has_value() && bool(__x.error() == __e.error()); } |
| |
| friend constexpr void |
| swap(expected& __x, expected& __y) |
| noexcept(noexcept(__x.swap(__y))) |
| requires requires { __x.swap(__y); } |
| { __x.swap(__y); } |
| |
| private: |
| template<typename, typename> friend class expected; |
| |
| template<typename _Vp> |
| constexpr void |
| _M_assign_unex(_Vp&& __v) |
| { |
| if (_M_has_value) |
| { |
| std::construct_at(__builtin_addressof(_M_unex), |
| std::forward<_Vp>(__v)); |
| _M_has_value = false; |
| } |
| else |
| _M_unex = std::forward<_Vp>(__v); |
| } |
| |
| |
| union { |
| struct { } _M_void; |
| _Er _M_unex; |
| }; |
| |
| bool _M_has_value; |
| }; |
| /// @} |
| |
| _GLIBCXX_END_NAMESPACE_VERSION |
| } // namespace std |
| |
| #endif // C++23 |
| #endif // _GLIBCXX_EXPECTED |