Relevant reading: https://quuxplusone.github.io/blog/2023/10/01/quick-custom-iterator/
The STL is "missing" a lot of custom iterator adaptors:
- emplace_iterator
- emplace_back_iterator
- emplace_front_iterator
- push_iterator
- pop_iterator
But they can all be written in just a few lines by hand (especially, for output iterators, by using the trick detailed above), so I think it's fine that they don't exist in the STL.

Here's your `pop_off` example in C++23: https://godbolt.org/z/3dPEqhn47

template<class Q>
struct PopIterator {
using difference_type = int;
using value_type = typename Q::value_type;
explicit PopIterator(Q& q) : q_(&q) {}
PopIterator& operator++() { q_->pop(); return *this; }
PopIterator operator++(int) { auto copy = *this; ++*this; return copy; }
decltype(auto) operator*() const { return q_->top(); }
bool operator==(std::default_sentinel_t) const { return q_->empty(); }
Q *q_;
};

template<class R>
auto pop_off(R&& rg) {
return stdr::subrange(PopIterator(rg), std::default_sentinel);
}

template<stdr::input_range R1, stdr::input_range R2>
bool is_permutation(R1&& r1, R2&& r2) {
return stdr::equal(
pop_off(r1 | stdr::to<std::priority_queue>()),
pop_off(r2 | stdr::to<std::priority_queue>())
);
}

–Arthur


On Sat, Feb 22, 2025 at 1:25 PM Ell via Std-Proposals <std-proposals@lists.isocpp.org> wrote:
This could be occasionally nice to have, especially if it worked with
other adaptors as well. Some random examples:

Finding the area of the largest rectangle under a histogram [1] can be
solved with a monotonic stack. Before pushing a new element, the
discarded elements could be processed as a range:

    template <stdr::input_range R>
    auto max_rect_area (R&& r) {
        using idx_t    = stdr::range_difference_t<R>;
        using height_t = stdr::range_value_t<R>;
        using area_t   = decltype (idx_t () * height_t ());

        struct bar {
            idx_t    idx;
            height_t height;
        };

        std::stack<bar> stk;
        area_t          result {};

        for (auto [idx, height] : stdv::concat (r, stdv::single (0)) |
                                  stdv::enumerate)
        {
            std::tie (idx, result) = stdr::fold_left (
                pop_off (stk) |                  // <-- popping adaptor
                stdv::take_while ([=] (bar b) {
                    return b.height >= height;
                }),

                std::pair (idx, result),

                [=] (auto p, bar b) {
                    return std::pair (
                        b.idx,
                        std::max (p.second, (idx - b.idx) * b.height)
                    );
                }
            );

            stk.emplace (idx, height);
        }

        return result;
    }

Finding minimal paths in a graph using BFS:

    std::queue<...> q;

    q.push (seed);

    for (std::size_t path_length = 0; ! q.empty (); ++path_length) {
        /* all current elements in the queue are path_length away
         * from seed
         */
        for (auto& node : pop_off (q) | stdv::take (q.size ())) {
            // process node, possibly pushing new elements
        }
    }

Using priority_queue as a lazily-sorted range:

    template <stdr::input_range R1, stdr::input_range R2>
    bool is_permutation (R1&& r1, R2&& r2) {
        return stdr::equal (
            pop_off (
                stdr::to<std::priority_queue> (std::forward<R1> (r1))
            ),
            pop_off (
                stdr::to<std::priority_queue> (std::forward<R2> (r2))
            )
        );
    }

[1] https://leetcode.com/problems/largest-rectangle-in-histogram/
--
Std-Proposals mailing list
Std-Proposals@lists.isocpp.org
https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals