Date: Wed, 29 Apr 2026 08:47:03 -0400
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.
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
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.
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 12:47:17
