util: Implement Expected::value()&& and Expected::error()&&
What changed, and why it matters
This commit adds two new ways to access values and errors from a custom 'Expected' helper type in Bitcoin Core: rvalue (move) overloads for value() and error(). It also tightens error() to use an assertion helper instead of a manual assert. The changes are purely internal utility improvements, currently unused elsewhere in the codebase, and do not fix or introduce any security vulnerability.
No security action required. This is a normal code-quality/utility enhancement. Reviewers may optionally verify the new overloads compile and behave correctly in CI.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch implements Expected
Changed components
src/util/expected.hsrc/test/util_expected_tests.cppInspect captured patch +27 / −12
diff --git a/src/test/util_expected_tests.cpp b/src/test/util_expected_tests.cpp
index e626a988..7c9d62ad 100644
--- a/src/test/util_expected_tests.cpp
+++ b/src/test/util_expected_tests.cpp
@@ -43,6 +43,13 @@ BOOST_AUTO_TEST_CASE(expected_value)
BOOST_CHECK_EQUAL(read->x, 45);
}
+BOOST_AUTO_TEST_CASE(expected_value_rvalue)
+{
+ Expected<std::unique_ptr<int>, int> no_copy{std::make_unique<int>(5)};
+ const auto moved{std::move(no_copy).value()};
+ BOOST_CHECK_EQUAL(*moved, 5);
+}
+
BOOST_AUTO_TEST_CASE(expected_value_or)
{
Expected<std::unique_ptr<int>, int> no_copy{std::make_unique<int>(1)};
@@ -81,6 +88,20 @@ BOOST_AUTO_TEST_CASE(expected_error)
BOOST_CHECK_EQUAL(read.error(), "fail1");
}
+BOOST_AUTO_TEST_CASE(expected_error_rvalue)
+{
+ {
+ Expected<int, std::unique_ptr<int>> nocopy_err{Unexpected{std::make_unique<int>(7)}};
+ const auto moved{std::move(nocopy_err).error()};
+ BOOST_CHECK_EQUAL(*moved, 7);
+ }
+ {
+ Expected<void, std::unique_ptr<int>> void_nocopy_err{Unexpected{std::make_unique<int>(9)}};
+ const auto moved{std::move(void_nocopy_err).error()};
+ BOOST_CHECK_EQUAL(*moved, 9);
+ }
+}
+
BOOST_AUTO_TEST_CASE(unexpected_error_accessors)
{
Unexpected u{std::make_unique<int>(-1)};
diff --git a/src/util/expected.h b/src/util/expected.h
index a7d02c39..85d4bf9d 100644
--- a/src/util/expected.h
+++ b/src/util/expected.h
@@ -57,20 +57,21 @@ public:
constexpr bool has_value() const noexcept { return m_data.index() == 0; }
constexpr explicit operator bool() const noexcept { return has_value(); }
- constexpr const T& value() const LIFETIMEBOUND
+ constexpr const T& value() const& LIFETIMEBOUND
{
if (!has_value()) {
throw BadExpectedAccess{};
}
return std::get<0>(m_data);
}
- constexpr T& value() LIFETIMEBOUND
+ constexpr T& value() & LIFETIMEBOUND
{
if (!has_value()) {
throw BadExpectedAccess{};
}
return std::get<0>(m_data);
}
+ constexpr T&& value() && LIFETIMEBOUND { return std::move(value()); }
template <class U>
T value_or(U&& default_value) const&
@@ -83,16 +84,9 @@ public:
return has_value() ? std::move(value()) : std::forward<U>(default_value);
}
- constexpr const E& error() const LIFETIMEBOUND
- {
- assert(!has_value());
- return std::get<1>(m_data);
- }
- constexpr E& error() LIFETIMEBOUND
- {
- assert(!has_value());
- return std::get<1>(m_data);
- }
+ 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()); }
constexpr T& operator*() noexcept LIFETIMEBOUND { return value(); }
constexpr const T& operator*() const noexcept LIFETIMEBOUND { return value(); }
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.