Repository: bitcoin/bitcoin
File: src/util/expected.h (C/C++)

Code before:
    {
        return has_value() ? value() : std::forward<U>(default_value);
    }
    template <class U>
    T value_or(U&& default_value) &&
    {
        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 T& operator*() LIFETIMEBOUND { return value(); }
    constexpr const T& operator*() const LIFETIMEBOUND { return value(); }

    constexpr T* operator->() LIFETIMEBOUND { return &value(); }
    constexpr const T* operator->() const LIFETIMEBOUND { return &value(); }
};

template <class E>
class Expected<void, E>
{
private:
    std::variant<std::monostate, E> m_data;

public:
    constexpr Expected() : m_data{std::in_place_index<0>, std::monostate{}} {}
    template <class Err>
    constexpr Expected(Unexpected<Err> 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 value() const
    {
        if (!Assume(has_value())) {
            throw BadExpectedAccess{};
        }
    }

    constexpr const E& error() const LIFETIMEBOUND

Review comment (refactor):
In commit "util: Add Expected<void, E> specialization" (fa3d6c760db3dafbaa530401893a40fc5f6575f9)

This specialization is more complicated than it needs to be. Would suggest simplifying with inheritance:

```c++
//! Specialization for void returning void from value methods.
template <class E>
class Expected<void, E> : public Expected<std::monostate, E>
{
    using Base = Expected<std::monostate, E>;
public:
    using Expected<std::monostate, E>::Expected;
    constexpr void operator*() const noexcept { Base::operator*(); }
    constexpr void value() const& { Base::value(); }
    constexpr void value() && { Base::value(); }
    void value_or() = delete;
};
```

Code after:
    T value_or(U&& default_value) const&
    {
        return has_value() ? value() : std::forward<U>(default_value);
    }
    template <class U>
    T value_or(U&& default_value) &&
    {
        return has_value() ? std::move(value()) : std::forward<U>(default_value);
    }

    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 void swap(Expected& other) noexcept { m_data.swap(other.m_data); }

    constexpr T& operator*() & noexcept LIFETIMEBOUND { return value(); }
    constexpr const T& operator*() const& noexcept LIFETIMEBOUND { return value(); }
    constexpr T&& operator*() && noexcept LIFETIMEBOUND { return std::move(value()); }

    constexpr T* operator->() noexcept LIFETIMEBOUND { return &value(); }
    constexpr const T* operator->() const noexcept LIFETIMEBOUND { return &value(); }
};

template <class E>
class Expected<void, E>
{
private:
    std::variant<std::monostate, E> m_data;

public:
    constexpr Expected() : m_data{std::in_place_index<0>, std::monostate{}} {}
    template <class Err>
    constexpr Expected(Unexpected<Err> 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()); }