C++ Logo

std-proposals

Advanced search

Re: [std-proposals] std::is_heap could take ForwardIterator, not RandomAccessIterator

From: Marcin Jaczewski <marcinjaczewski86_at_[hidden]>
Date: Wed, 29 Apr 2026 15:17:02 +0200
śr., 29 kwi 2026 o 14:47 Arthur O'Dwyer <arthur.j.odwyer_at_[hidden]> napisał(a):
>
> On Wed, Apr 29, 2026 at 4:51 AM Marcin Jaczewski <marcinjaczewski86_at_[hidden]> wrote:
>>
>> śr., 29 kwi 2026 o 03:23 Arthur O'Dwyer via Std-Proposals <std-proposals_at_[hidden]> napisał(a):
>> >
>> > Has anyone previously brought up that `std::is_heap` is currently specified to take random-access iterators, but in fact (AFAICT) can easily be implemented to use only forward-iterator operations?
>>
>> But any other operation on this heap needs random-access, right?
>> If this is true then the forward version will not be very useful as
>> any other operation will be not available until you
>> convert to some random access container.
>
>
> That's right (or at least, I'm not considering/proposing to weaken the requirements on any other algorithm with "heap" in its name).
> Where this came up for me in real life was in a libc++ test, where we have essentially
>
> template <class Adaptor>
>
> void test_push_range(bool is_result_heapified) {
>
> ~~~~
>
> Adaptor adaptor(test_case.initial.begin(), test_case.initial.end());
>
> ~~~~
>
> auto& c = ~~~~.get_container();
>
>
> if (is_result_heapified) {
>
> assert(std::ranges::is_heap(c));
>
> return std::ranges::is_permutation(c, test_case.expected);
>
> } else {
>
> return std::ranges::equal(c, test_case.expected);
>
> }
>
> This template is used in the test suite for `std::queue` and in the test suite for `std::priority_queue`. (In the former with is_result_heapified=false; in the latter with is_result_heapified=true.)
> By `std::queue` naturally I mean `std::queue<T, std::deque<T>>` — that's the default underlying container for `queue`.
> This is fine, today, because `deque` is a random-access container.
> But suppose I want to extend the tests to also cover `Adaptor = std::queue<T, std::list<T>>`. Then this template breaks; and it happens that the solitary reason is `std::ranges::is_heap(c)` doesn't compile anymore.
>

But is it even possible to call this function with
`is_result_heapified = true` and `std::queue<T, std::list<T>>` in any
case?

If not, then better would be "delete" this code by adding `if
constexpr (requires ...)` to replace this case by exception or
`assert(false)` as this branch does not make any sense.

In the opposite case then this could be a valid case to reduce the
iterator type.

> Now, obviously the constraint on is_heap isn't the original sin here. Probably `test_push_range` ought to pass `is_result_heapified` as a template parameter instead of a function parameter, and then use `if constexpr` instead of `if` for that conditional. That would fix things, now that we know there's a problem. But aesthetically I'm displeased that there was any problem! There doesn't seem to be any good reason the code didn't Just Work as originally written! It seems like perfectly reasonable STL-style generic code to me.
> Which does make me wonder if I'm missing something subtle. IIUC your suggestion is that is_heap was originally overconstrained simply for aesthetic reasons — to match push_heap and pop_heap — and I agree that's plausible, but could there be some technical reason I haven't spotted yet?
>
> Cheers,
> Arthur

Received on 2026-04-29 13:17:17