diff --git a/src/test/util_expected_tests.cpp b/src/test/util_expected_tests.cpp index 64f543bf3fd..e626a9887c7 100644 --- a/src/test/util_expected_tests.cpp +++ b/src/test/util_expected_tests.cpp @@ -66,6 +66,8 @@ BOOST_AUTO_TEST_CASE(expected_error) { Expected e{}; BOOST_CHECK(e.has_value()); + [&]() -> void { return e.value(); }(); // check value returns void and does not throw + [&]() -> void { return *e; }(); e = Unexpected{"fail"}; BOOST_CHECK(!e.has_value()); diff --git a/src/util/expected.h b/src/util/expected.h index d4611698607..a7d02c39dcc 100644 --- a/src/util/expected.h +++ b/src/util/expected.h @@ -6,10 +6,10 @@ #define BITCOIN_UTIL_EXPECTED_H #include +#include #include #include -#include #include #include @@ -44,28 +44,27 @@ template class Expected { private: - using ValueType = std::conditional_t, std::monostate, T>; - std::variant m_data; + std::variant m_data; public: - constexpr Expected() : m_data{std::in_place_index_t<0>{}, ValueType{}} {} - constexpr Expected(ValueType v) : m_data{std::in_place_index_t<0>{}, std::move(v)} {} + constexpr Expected() : m_data{std::in_place_index<0>, T{}} {} + constexpr Expected(T v) : m_data{std::in_place_index<0>, std::move(v)} {} template - constexpr Expected(Unexpected u) : m_data{std::in_place_index_t<1>{}, std::move(u).error()} + constexpr Expected(Unexpected u) : m_data{std::in_place_index<1>, std::move(u).error()} { } constexpr bool has_value() const noexcept { return m_data.index() == 0; } constexpr explicit operator bool() const noexcept { return has_value(); } - constexpr const ValueType& value() const LIFETIMEBOUND + constexpr const T& value() const LIFETIMEBOUND { if (!has_value()) { throw BadExpectedAccess{}; } return std::get<0>(m_data); } - constexpr ValueType& value() LIFETIMEBOUND + constexpr T& value() LIFETIMEBOUND { if (!has_value()) { throw BadExpectedAccess{}; @@ -74,12 +73,12 @@ public: } template - ValueType value_or(U&& default_value) const& + T value_or(U&& default_value) const& { return has_value() ? value() : std::forward(default_value); } template - ValueType value_or(U&& default_value) && + T value_or(U&& default_value) && { return has_value() ? std::move(value()) : std::forward(default_value); } @@ -95,11 +94,40 @@ public: return std::get<1>(m_data); } - constexpr ValueType& operator*() noexcept LIFETIMEBOUND { return value(); } - constexpr const ValueType& operator*() const noexcept LIFETIMEBOUND { return value(); } + constexpr T& operator*() noexcept LIFETIMEBOUND { return value(); } + constexpr const T& operator*() const noexcept LIFETIMEBOUND { return value(); } - constexpr ValueType* operator->() noexcept LIFETIMEBOUND { return &value(); } - constexpr const ValueType* operator->() const noexcept LIFETIMEBOUND { return &value(); } + constexpr T* operator->() noexcept LIFETIMEBOUND { return &value(); } + constexpr const T* operator->() const noexcept LIFETIMEBOUND { return &value(); } +}; + +template +class Expected +{ +private: + std::variant m_data; + +public: + constexpr Expected() : m_data{std::in_place_index<0>, std::monostate{}} {} + template + constexpr Expected(Unexpected u) : m_data{std::in_place_index<1>, std::move(u).error()} + { + } + + constexpr bool has_value() const noexcept { return m_data.index() == 0; } + constexpr explicit operator bool() const noexcept { return has_value(); } + + constexpr void operator*() const noexcept { return value(); } + constexpr void value() const + { + if (!has_value()) { + throw BadExpectedAccess{}; + } + } + + constexpr const E& error() const& noexcept LIFETIMEBOUND { return *Assert(std::get_if<1>(&m_data)); } + constexpr E& error() & noexcept LIFETIMEBOUND { return *Assert(std::get_if<1>(&m_data)); } + constexpr E&& error() && noexcept LIFETIMEBOUND { return std::move(error()); } }; } // namespace util