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

Code before:
    void read(Span<std::byte> dst) { GetStream().read(dst); }
    void ignore(size_t num) { GetStream().ignore(num); }
    bool eof() const { return GetStream().eof(); }
    size_t size() const { return GetStream().size(); }

    //! Get reference to stream parameters.
    template <typename P>
    const auto& GetParams() const
    {
        if constexpr (std::is_convertible_v<Params, P>) {
            return m_params;
        } else {
            return m_substream.template GetParams<P>();
        }
    }

    //! Get reference to underlying stream.
    auto& GetStream()
    {
        if constexpr (ContainsStream<SubStream>) {
            return m_substream.GetStream();
        } else {
            return m_substream;
        }
    }
    auto& GetStream() const
    {
        if constexpr (ContainsStream<SubStream>) {
            return m_substream.GetStream();
        } else {
            return m_substream;
        }
    }
};

/**
 * Explicit template deduction guide is required for single-parameter
 * constructor so Substream&& is treated as a forwarding reference, and
 * SubStream is deduced as reference type for lvalue arguments.
 */
template <typename Substream, typename Params>
ParamsStream(Substream&&, const Params&) -> ParamsStream<Substream, Params>;

/**
 * Template deduction guide for multiple params arguments that creates a nested
 * ParamsStream.
 */
template <typename Substream, typename Params1, typename Params2, typename... Params>
ParamsStream(Substream&& s, const Params1& params1, const Params2& params2, const Params&... params) ->
    ParamsStream<decltype(ParamsStream{std::forward<Substream>(s), params2, params...}), Params1>;


Review comment (bug):
If `m_substream` is an owned instance, it will become `const SubStream` in const member functions, and the return value here will be `const Substream&`.
However, if `m_substream` is a reference/pointer, the constness is applied to the reference part, so the method will return a mutable reference.
See https://stackoverflow.com/questions/5055427/returning-non-const-reference-from-a-const-member-function

My suggestion would be to return `const auto&` here unless the intent is to enable mutability (which only happens when the member is a reference).

#### Illustrative example
```C++
class A
{
    int& b;
public:
    A(int& n) : b(n) {}
    auto& foo() const { return b; }
};

    ....
    int bar = 123;
    const A a(bar);
    int& mutating = a.foo();
    mutating = 2;
```

Code after:
    void read(Span<std::byte> dst) { GetStream().read(dst); }
    void ignore(size_t num) { GetStream().ignore(num); }
    bool eof() const { return GetStream().eof(); }
    size_t size() const { return GetStream().size(); }

    //! Get reference to stream parameters.
    template <typename P>
    const auto& GetParams() const
    {
        if constexpr (std::is_convertible_v<Params, P>) {
            return m_params;
        } else {
            return m_substream.template GetParams<P>();
        }
    }

    //! Get reference to underlying stream.
    auto& GetStream()
    {
        if constexpr (ContainsStream<SubStream>) {
            return m_substream.GetStream();
        } else {
            return m_substream;
        }
    }
    const auto& GetStream() const
    {
        if constexpr (ContainsStream<SubStream>) {
            return m_substream.GetStream();
        } else {
            return m_substream;
        }
    }
};

/**
 * Explicit template deduction guide is required for single-parameter
 * constructor so Substream&& is treated as a forwarding reference, and
 * SubStream is deduced as reference type for lvalue arguments.
 */
template <typename Substream, typename Params>
ParamsStream(Substream&&, const Params&) -> ParamsStream<Substream, Params>;

/**
 * Template deduction guide for multiple params arguments that creates a nested
 * ParamsStream.
 */
template <typename Substream, typename Params1, typename Params2, typename... Params>
ParamsStream(Substream&& s, const Params1& params1, const Params2& params2, const Params&... params) ->
    ParamsStream<decltype(ParamsStream{std::forward<Substream>(s), params2, params...}), Params1>;