std::ranges::advance and std::ranges::next when the difference type is also a sentinel typeSection: 24.4.4.2 [range.iter.op.advance], 24.4.4.4 [range.iter.op.next] Status: New Submitter: Jiang An Opened: 2026-01-09 Last modified: 2026-02-20
Priority: 3
View all other issues in [range.iter.op.advance].
View all issues with New status.
Discussion:
Currently, ranges::advance and ranges::next have operator() overloads that accept
(iterator, sentinel) and (iterator, difference). However, when the difference type
of the iterator is also a sentinel type, there may be ambiguity in both overloads.
#include <cstddef>
#include <iterator>
#include <type_traits>
template<class T>
struct FwdIter { // triggers ADL for T
FwdIter();
using value_type = std::remove_cv_t<T>;
using difference_type = int;
T& operator*() const;
FwdIter& operator++();
FwdIter operator++(int);
friend bool operator==(const FwdIter&, const FwdIter&);
};
static_assert(std::forward_iterator<FwdIter<int>>);
struct OmniConv {
OmniConv(const auto&);
friend bool operator==(OmniConv, OmniConv); // found by ADL via things related to OmniConv
};
int main() {
FwdIter<OmniConv> it{};
std::ranges::advance(it, 0); // ambiguous
std::ranges::next(it, 0); // ambiguous
}
Perhaps it would be better to ensure that calling ranges::advance or ranges::next
with an iterator value and a value of the difference type is unambiguous. Note that
wrapping iterators that trigger ADL for the value type like the FwdIter in this example
are common in standard library implementations, so ambiguity can be easily raised from
containers with such OmniConv being their element type.
[2026-02-20; Reflector poll.]
Set priority to 3 after reflector poll.
The type in the example is basically a std::any that additionally type-erases
equality. That causes a problem for templated iterators that have their
value type's namespace as an associated namespace for ADL.
Instead of allowing integers to be sentinels in general but just restricting
them from being used with these overloads, we could just change the concept
so that integers do not model sentinel_for.
That's similar to what we did for span's constructor which has a similar
problem (though that one's more aggressive due to compatibility constraints).
Previous resolution [SUPERSEDED]:
This wording is relative to N5032.
Modify 24.2 [iterator.synopsis], header
<iterator>synopsis, as indicated:[…] namespace std { template<class T> using with-reference = T&; // exposition only template<class T> concept can-reference // exposition only = requires { typename with-reference<T>; }; template<class T> concept dereferenceable // exposition only = requires(T& t) { { *t } -> can-reference; // not required to be equality-preserving }; template<class T> constexpr bool is-integer-like = see below; // exposition only […] // 24.4.4 [range.iter.ops], range iterator operations namespace ranges { // 24.4.4.2 [range.iter.op.advance], ranges::advance template<input_or_output_iterator I> constexpr void advance(I& i, iter_difference_t<I> n); // freestanding template<input_or_output_iterator I,sentinel_for<I>class S> requires (!is-integer-like<S>) && sentinel_for<S, I> constexpr void advance(I& i, S bound); // freestanding template<input_or_output_iterator I, sentinel_for<I> S> constexpr iter_difference_t<I> advance(I& i, iter_difference_t<I> n, // freestanding S bound); […] // 24.4.4.4 [range.iter.op.next], ranges::next template<input_or_output_iterator I> constexpr I next(I x); // freestanding template<input_or_output_iterator I> constexpr I next(I x, iter_difference_t<I> n); // freestanding template<input_or_output_iterator I,sentinel_for<I>class S> requires (!is-integer-like<S>) && sentinel_for<S, I> constexpr I next(I x, S bound); // freestanding template<input_or_output_iterator I, sentinel_for<I> S> constexpr I next(I x, iter_difference_t<I> n, S bound); // freestanding } […] }Modify 24.4.4.2 [range.iter.op.advance] as indicated:
template<input_or_output_iterator I,sentinel_for<I>class S> requires (!is-integer-like<S>) && sentinel_for<S, I> constexpr void advance(I& i, S bound);-3- Preconditions: […]
Modify 24.4.4.4 [range.iter.op.next] as indicated:
template<input_or_output_iterator I,sentinel_for<I>class S> requires (!is-integer-like<S>) && sentinel_for<S, I> constexpr I next(I x, S bound);-3- Effects: […]
[2026-02-20; Jonathan provides new wording]
Proposed resolution:
This wording is relative to N5032.
Modify 24.3.4.7 [iterator.concept.sentinel] as indicated:
template<class S, class I>
concept sentinel_for =
semiregular<S> &&
!is-integer-like<S> &&
input_or_output_iterator<I> &&
weakly-equality-comparable-with<S, I>; // see 18.5.4 [concept.equalitycomparable]