What's the purpose of the exposition-only iterator-sentinel-pair concept as defined in [range.subrange]?
template<class T>
concept iterator-sentinel-pair = // exposition only
!range<T> && pair-like<T> &&
sentinel_for<tuple_element_t<1, T>, tuple_element_t<0, T>>;
The only time it's used, as far as I can see, is for CTAD, such as in
template<iterator-sentinel-pair P>
subrange(P) -> subrange<tuple_element_t<0, P>, tuple_element_t<1, P>>;
However, there aren't any constructors of std::ranges::subrange that will work with this concept: the only real constructor possibly fitting this parameter list is this one:
template<not-same-as<subrange> R>
requires borrowed_range<R> &&
convertible-to-non-slicing<iterator_t<R>, I> &&
convertible_to<sentinel_t<R>, S>
constexpr subrange(R&& r) requires (!StoreSize || sized_range<R>);
which requires (through borrowed_range<R>) that we have range<R>... but iterator-sentinel-pair explicitly requires not range<R>!
It looks to me that the intention was to allow code along the lines of
std::multiset foo = // ...
auto result = foo.equal_range(key); // an iterator-sentinel pair
for (auto value : std::ranges::subrange(result)) {
// ...
}
but this clearly will not compile, as std::pair<It, It> doesn't fulfil the requirements for std::range. Maybe the constructor to allow for this use-case was overlooked?
Is there some other purpose I'm missing?