This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of New status.
sized-random-access-range and bidirectional-common
in <ranges>Section: 25.2 [ranges.syn], 25.4.6 [range.refinements], 25.5.3.1 [view.interface.general], 25.7.10.1 [range.take.overview], 25.7.12.1 [range.drop.overview], 25.7.12.2 [range.drop.view], 25.7.14.3 [range.join.iterator], 25.7.15.2 [range.join.with.view], 25.7.15.3 [range.join.with.iterator], 25.7.20.2 [range.common.view], 25.7.25.2 [range.zip.view], 25.7.30.2 [range.slide.view], 25.7.33.2 [range.cartesian.view] Status: New Submitter: Hewill Kang Opened: 2025-07-28 Last modified: 2025-10-23
Priority: 4
View other active issues in [ranges.syn].
View all other issues in [ranges.syn].
View all issues with New status.
Discussion:
P3179 introduces a new
exposition-only concept, sized-random-access-range, into <ranges> to simplify
the signature of parallel range algorithms.
However, this also applies more broadly to <ranges>, as we often need to determine whether a range satisfies
both sized_range and random_access_range in order to dispatch optimized branches.
common_range and
bidirectional_range to decide whether to provide backward traversal capabilities,
which is expressed by the exposition-only concept bidirectional-common introduced in
P2441.
Unfortunately, this concept currently only applies to a single section.
It would be much simpler and more readable if both concepts were available throughout <ranges>,
which would also allow newly introduced adapters or other library features to take advantage of them.
Note that since some of these simplifications change the order in which the concepts are spelled, they may not be
purely editorial.
[2025-10-23; Reflector poll.]
Set priority to 4 after reflector poll.
Use of exposition-only concepts is not necessary improving readability or produced error messages.
Proposed resolution:
This wording is relative to N5014.
Modify 25.2 [ranges.syn], header <ranges>
synopsis, as indicated:
// mostly freestanding #include <compare> // see 17.12.1 [compare.syn] #include <initializer_list> // see 17.11.2 [initializer.list.syn] #include <iterator> // see 24.2 [iterator.synopsis] namespace std::ranges { […] template<class T> concept sized-random-access-range = see below; // exposition only template<class T> concept common-bidirectional-range = see below; // exposition only […] }
Modify 25.4.6 [range.refinements] as indicated:
[…]-8- The exposition-only concept
sized-random-access-rangespecifies the requirements of arangetype that is sized and allows random access to its elements.template<class T> concept sized-random-access-range = // exposition only random_access_range<T> && sized_range<T>;[Note 1: This concept constrains some parallel algorithm overloads; see [algorithms]. — end note]
-?- The exposition-only conceptcommon-bidirectional-rangespecifies the requirements of arangetype that is bidirectional and whose iterator and sentinel types are the same.template<class T> concept common-bidirectional-range = // exposition only bidirectional_range<T> && common_range<T>;
Modify 25.5.3.1 [view.interface.general] as indicated:
-1- The class template
view_interfaceis a helper for defining view-like types that offer a container-like interface. It is parameterized with the type that is derived from it.namespace std::ranges { template<class D> requires is_class_v<D> && same_as<D, remove_cv_t<D>> class view_interface { private: […] public: […] constexpr decltype(auto) back() requiresbidirectional_range<D> && common_range<D>common-bidirectional-range<D>; constexpr decltype(auto) back() const requiresbidirectional_range<const D> && common_range<const D>common-bidirectional-range<const D>; […] }; }
Modify 25.5.3.2 [view.interface.members] as indicated:
constexpr decltype(auto) back() requiresbidirectional_range<D> && common_range<D>common-bidirectional-range<D>; constexpr decltype(auto) back() const requiresbidirectional_range<const D> && common_range<const D>common-bidirectional-range<const D>;-3- Hardened preconditions:
-4- Effects: Equivalent to:!empty()istrue.return *ranges::prev(ranges::end(derived()));
Modify 25.7.10.1 [range.take.overview] as indicated:
-2- The name
views::takedenotes a range adaptor object (25.7.2 [range.adaptor.object]). LetEandFbe expressions, letTberemove_cvref_t<decltype((E))>, and letDberange_difference_t<decltype((E))>. Ifdecltype((F))does not modelconvertible_to<D>,views::take(E, F)is ill-formed. Otherwise, the expressionviews::take(E, F)is expression-equivalent to:
- […]
(2.2) — Otherwise, if
[…]Tmodelsrandom_access_rangeandsized_rangesized-random-access-rangeand is a specialization ofspan(23.7.2.2 [views.span]),basic_string_view(27.3 [string.view]), orranges::subrange(25.5.4 [range.subrange]), thenU(ranges::begin(E), ranges::begin(E) + std::min<D>(ranges::distance(E), F)), except thatEis evaluated only once, whereUis a type determined as follows:(2.3) — otherwise, if
Tis a specialization ofiota_view(25.6.4.2 [range.iota.view]) that modelsrandom_access_rangeandsized_rangesized-random-access-range, theniota_view(*ranges::begin(E), *(ranges::begin(E) + std::min<D>(ranges::distance(E), F))), except thatEis evaluated only once.
Modify 25.7.12.1 [range.drop.overview] as indicated:
-2- The name
views::dropdenotes a range adaptor object (25.7.2 [range.adaptor.object]). LetEandFbe expressions, letTberemove_cvref_t<decltype((E))>, and letDberange_difference_t<decltype((E))>. Ifdecltype((F))does not modelconvertible_to<D>,views::drop(E, F)is ill-formed. Otherwise, the expressionviews::drop(E, F)is expression-equivalent to:
- […]
(2.2) — Otherwise, if
[…]Tmodelsrandom_access_rangeandsized_rangesized-random-access-rangeand is(2.3) — Otherwise, if
Tis a specialization ofsubrange(25.5.4 [range.subrange]) that modelsrandom_access_rangeandsized_rangesized-random-access-range, thenT(ranges::begin(E) + std::min<D>(ranges::distance(E), F), ranges::end(E), to-unsigned-like(ranges::distance(E) - std::min<D>(ranges::distance(E), F))), except thatEandFare each evaluated only once.
Modify 25.7.12.2 [range.drop.view] as indicated:
namespace std::ranges { template<view V> class drop_view : public view_interface<drop_view<V>> { public: […] constexpr auto begin() requires (!(simple-view<V> &&[…]random_access_range<const V> && sized_range<const V>sized-random-access-range<const V>)); constexpr auto begin() const requiresrandom_access_range<const V> && sized_range<const V>sized-random-access-range<const V>; […] }; […] }constexpr auto begin() requires (!(simple-view<V> &&random_access_range<const V> && sized_range<const V>sized-random-access-range<const V>)); constexpr auto begin() const requiresrandom_access_range<const V> && sized_range<const V>sized-random-access-range<const V>;-3- Returns:
ranges::next(ranges::begin(base_), count_, ranges::end(base_)).
Modify 25.7.14.3 [range.join.iterator] as indicated:
namespace std::ranges { template<input_range V> requires view<V> && input_range<range_reference_t<V>> template<bool Const> struct join_view<V>::iterator { private: […] public: […] constexpr iterator& operator--() requires ref-is-glvalue && bidirectional_range<Base> &&bidirectional_range<range_reference_t<Base>> && common_range<range_reference_t<Base>>common-bidirectional-range<range_reference_t<Base>>; constexpr iterator operator--(int) requires ref-is-glvalue && bidirectional_range<Base> &&bidirectional_range<range_reference_t<Base>> && common_range<range_reference_t<Base>>common-bidirectional-range<range_reference_t<Base>>; […] }; }-1-
iterator::iterator_conceptis defined as follows::[…]
(1.1) — If
[…]ref-is-glvalueistrue,Basemodelsbidirectional_range, andrange_reference_t<Base>modelsbothbidirectional_rangeandcommon_rangecommon-bidirectional-range, theniterator_conceptdenotesbidirectional_iterator_tag.constexpr iterator& operator--() requires ref-is-glvalue && bidirectional_range<Base> &&bidirectional_range<range_reference_t<Base>> && common_range<range_reference_t<Base>>common-bidirectional-range<range_reference_t<Base>>;-16- Effects: Equivalent to:
[…]constexpr iterator operator--(int) requires ref-is-glvalue && bidirectional_range<Base> &&bidirectional_range<range_reference_t<Base>> && common_range<range_reference_t<Base>>common-bidirectional-range<range_reference_t<Base>>;-17- Effects: Equivalent to:
[…]
Modify 25.7.15.2 [range.join.with.view] as indicated:
namespace std::ranges {
template<class R>
concept bidirectional-common = bidirectional_range<R> && common_range<R>; // exposition only
[…]
}
Modify 25.7.15.3 [range.join.with.iterator] as indicated:
namespace std::ranges { template<input_range V, forward_range Pattern> requires view<V> && input_range<range_reference_t<V>> && view<Pattern> && concatable<range_reference_t<V>, Pattern> template<bool Const> class join_with_view<V, Pattern>::iterator { private: […] public: […] constexpr iterator& operator--() requires ref-is-glvalue && bidirectional_range<Base> &&bidirectional-commoncommon-bidirectional-range<InnerBase> &&bidirectional-commoncommon-bidirectional-range<PatternBase>; constexpr iterator operator--(int) requires ref-is-glvalue && bidirectional_range<Base> &&bidirectional-commoncommon-bidirectional-range<InnerBase> &&bidirectional-commoncommon-bidirectional-range<PatternBase>; […] }; }-1-
iterator::iterator_conceptis defined as follows::[…]
(1.1) — If
[…]ref-is-glvalueistrue,Basemodelsbidirectional_range, andInnerBaseandPatternBaseeach modelbidirectional-commoncommon-bidirectional-range, theniterator_conceptdenotesbidirectional_iterator_tag.constexpr iterator& operator--() requires ref-is-glvalue && bidirectional_range<Base> &&bidirectional-commoncommon-bidirectional-range<InnerBase> &&bidirectional-commoncommon-bidirectional-range<PatternBase>;[…]-16- Effects: Equivalent to:
constexpr iterator operator--(int) requires ref-is-glvalue && bidirectional_range<Base> &&bidirectional-commoncommon-bidirectional-range<InnerBase> &&bidirectional-commoncommon-bidirectional-range<PatternBase>;-17- Effects: Equivalent to:
[…]
Modify 25.7.20.2 [range.common.view] as indicated:
namespace std::ranges {
template<view V>
requires (!common_range<V> && copyable<iterator_t<V>>)
class common_view : public view_interface<common_view<V>> {
private:
V base_ = V(); // exposition only
public:
[…]
constexpr auto begin() requires (!simple-view<V>) {
if constexpr (random_access_range<V> && sized_range<V>sized-random-access-range<V>)
return ranges::begin(base_);
else
return common_iterator<iterator_t<V>, sentinel_t<V>>(ranges::begin(base_));
}
constexpr auto begin() const requires range<const V> {
if constexpr (random_access_range<const V> && sized_range<const V>sized-random-access-range<const V>)
return ranges::begin(base_);
else
return common_iterator<iterator_t<const V>, sentinel_t<const V>>(ranges::begin(base_));
}
constexpr auto end() requires (!simple-view<V>) {
if constexpr (random_access_range<V> && sized_range<V>sized-random-access-range<V>)
return ranges::begin(base_) + ranges::distance(base_);
else
return common_iterator<iterator_t<V>, sentinel_t<V>>(ranges::end(base_));
}
constexpr auto end() const requires range<const V> {
if constexpr (random_access_range<const V> && sized_range<const V>sized-random-access-range<const V>)
return ranges::begin(base_) + ranges::distance(base_);
else
return common_iterator<iterator_t<const V>, sentinel_t<const V>>(ranges::end(base_));
}
[…]
};
[…]
}
Modify 25.7.25.2 [range.zip.view] as indicated:
namespace std::ranges {
template<class... Rs>
concept zip-is-common = // exposition only
(sizeof...(Rs) == 1 && (common_range<Rs> && ...)) ||
(!(bidirectional_range<Rs> && ...) && (common_range<Rs> && ...)) ||
((random_access_range<Rs> && ...) && (sized_range<Rs> && ...)sized-random-access-range<Rs> && ...);
[…]
}
Modify 25.7.30.2 [range.slide.view] as indicated:
namespace std::ranges {
template<class V>
concept slide-caches-nothing = random_access_range<V> && sized_range<V>sized-random-access-range<V>; // exposition only
template<class V>
concept slide-caches-last = // exposition only
!slide-caches-nothing<V> && bidirectional_range<V> && common_range<V>common-bidirectional-range<V>;
[…]
}
Modify 25.7.33.2 [range.cartesian.view] as indicated:
namespace std::ranges {
template<bool Const, class First, class... Vs>
concept cartesian-product-is-random-access = // exposition only
(random_access_range<maybe-const<Const, First>> && ... &&
(random_access_range<maybe-const<Const, Vs>>
&& sized_range<maybe-const<Const, Vs>>)sized-random-access-range<maybe-const<Const, Vs>>);
template<class R>
concept cartesian-product-common-arg = // exposition only
common_range<R> || (sized_range<R> && random_access_range<R>)sized-random-access-range<R>;
[…]
}