26 Ranges library [ranges]

26.5 Range utilities [range.utility]

26.5.1 General [range.utility.general]

The components in [range.utility] are general utilities for representing and manipulating ranges.

26.5.2 Helper concepts [range.utility.helpers]

Many of the types in subclause [range.utility] are specified in terms of the following exposition-only concepts: template<class R> concept simple-view = // exposition only view<R> && range<const R> && same_­as<iterator_t<R>, iterator_t<const R>> && same_­as<sentinel_t<R>, sentinel_t<const R>>; template<class I> concept has-arrow = // exposition only input_­iterator<I> && (is_pointer_v<I> || requires(I i) { i.operator->(); }); template<class T, class U> concept different-from = // exposition only !same_­as<remove_cvref_t<T>, remove_cvref_t<U>>;

26.5.3 View interface [view.interface]

26.5.3.1 General [view.interface.general]

The class template view_­interface is 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: constexpr D& derived() noexcept { // exposition only return static_cast<D&>(*this); } constexpr const D& derived() const noexcept { // exposition only return static_cast<const D&>(*this); } public: constexpr bool empty() requires sized_­range<D> || forward_­range<D> { if constexpr (sized_­range<D>) return ranges::size(derived()) == 0; else return ranges::begin(derived()) == ranges::end(derived()); } constexpr bool empty() const requires sized_­range<const D> || forward_­range<const D> { if constexpr (sized_­range<const D>) return ranges::size(derived()) == 0; else return ranges::begin(derived()) == ranges::end(derived()); } constexpr auto cbegin() { return ranges::cbegin(derived()); } constexpr auto cbegin() const requires range<const D> { return ranges::cbegin(derived()); } constexpr auto cend() { return ranges::cend(derived()); } constexpr auto cend() const requires range<const D> { return ranges::cend(derived()); } constexpr explicit operator bool() requires requires { ranges::empty(derived()); } { return !ranges::empty(derived()); } constexpr explicit operator bool() const requires requires { ranges::empty(derived()); } { return !ranges::empty(derived()); } constexpr auto data() requires contiguous_­iterator<iterator_t<D>> { return to_address(ranges::begin(derived())); } constexpr auto data() const requires range<const D> && contiguous_­iterator<iterator_t<const D>> { return to_address(ranges::begin(derived())); } constexpr auto size() requires forward_­range<D> && sized_­sentinel_­for<sentinel_t<D>, iterator_t<D>> { return ranges::end(derived()) - ranges::begin(derived()); } constexpr auto size() const requires forward_­range<const D> && sized_­sentinel_­for<sentinel_t<const D>, iterator_t<const D>> { return ranges::end(derived()) - ranges::begin(derived()); } constexpr decltype(auto) front() requires forward_­range<D>; constexpr decltype(auto) front() const requires forward_­range<const D>; constexpr decltype(auto) back() requires bidirectional_­range<D> && common_­range<D>; constexpr decltype(auto) back() const requires bidirectional_­range<const D> && common_­range<const D>; template<random_­access_­range R = D> constexpr decltype(auto) operator[](range_difference_t<R> n) { return ranges::begin(derived())[n]; } template<random_­access_­range R = const D> constexpr decltype(auto) operator[](range_difference_t<R> n) const { return ranges::begin(derived())[n]; } }; }
The template parameter D for view_­interface may be an incomplete type.
Before any member of the resulting specialization of view_­interface other than special member functions is referenced, D shall be complete, and model both derived_­from<view_­interface<D>> and view.

26.5.3.2 Members [view.interface.members]

constexpr decltype(auto) front() requires forward_­range<D>; constexpr decltype(auto) front() const requires forward_­range<const D>;
Preconditions: !empty() is true.
Effects: Equivalent to: return *ranges​::​begin(derived());
constexpr decltype(auto) back() requires bidirectional_­range<D> && common_­range<D>; constexpr decltype(auto) back() const requires bidirectional_­range<const D> && common_­range<const D>;
Preconditions: !empty() is true.
Effects: Equivalent to: return *ranges​::​prev(ranges​::​end(derived()));

26.5.4 Sub-ranges [range.subrange]

26.5.4.1 General [range.subrange.general]

The subrange class template combines together an iterator and a sentinel into a single object that models the view concept.
Additionally, it models the sized_­range concept when the final template parameter is subrange_­kind​::​sized.
namespace std::ranges { template<class From, class To> concept uses-nonqualification-pointer-conversion = // exposition only is_pointer_v<From> && is_pointer_v<To> && !convertible_­to<remove_pointer_t<From>(*)[], remove_pointer_t<To>(*)[]>; template<class From, class To> concept convertible-to-non-slicing = // exposition only convertible_­to<From, To> && !uses-nonqualification-pointer-conversion<decay_t<From>, decay_t<To>>; template<class T, class U, class V> concept pair-like-convertible-from = // exposition only !range<T> && !is_reference_v<T> && pair-like<T> && constructible_­from<T, U, V> && convertible-to-non-slicing<U, tuple_element_t<0, T>> && convertible_­to<V, tuple_element_t<1, T>>; template<input_­or_­output_­iterator I, sentinel_­for<I> S = I, subrange_kind K = sized_­sentinel_­for<S, I> ? subrange_kind::sized : subrange_kind::unsized> requires (K == subrange_kind::sized || !sized_­sentinel_­for<S, I>) class subrange : public view_interface<subrange<I, S, K>> { private: static constexpr bool StoreSize = // exposition only K == subrange_kind::sized && !sized_­sentinel_­for<S, I>; I begin_ = I(); // exposition only S end_ = S(); // exposition only make-unsigned-like-t<iter_difference_t<I>> size_ = 0; // exposition only; present only // when StoreSize is true public: subrange() requires default_­initializable<I> = default; constexpr subrange(convertible-to-non-slicing<I> auto i, S s) requires (!StoreSize); constexpr subrange(convertible-to-non-slicing<I> auto i, S s, make-unsigned-like-t<iter_difference_t<I>> n) requires (K == subrange_kind::sized); template<different-from<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>); template<borrowed_­range R> requires convertible-to-non-slicing<iterator_t<R>, I> && convertible_­to<sentinel_t<R>, S> constexpr subrange(R&& r, make-unsigned-like-t<iter_difference_t<I>> n) requires (K == subrange_kind::sized) : subrange{ranges::begin(r), ranges::end(r), n} {} template<different-from<subrange> PairLike> requires pair-like-convertible-from<PairLike, const I&, const S&> constexpr operator PairLike() const; constexpr I begin() const requires copyable<I>; [[nodiscard]] constexpr I begin() requires (!copyable<I>); constexpr S end() const; constexpr bool empty() const; constexpr make-unsigned-like-t<iter_difference_t<I>> size() const requires (K == subrange_kind::sized); [[nodiscard]] constexpr subrange next(iter_difference_t<I> n = 1) const & requires forward_­iterator<I>; [[nodiscard]] constexpr subrange next(iter_difference_t<I> n = 1) &&; [[nodiscard]] constexpr subrange prev(iter_difference_t<I> n = 1) const requires bidirectional_­iterator<I>; constexpr subrange& advance(iter_difference_t<I> n); }; template<input_­or_­output_­iterator I, sentinel_­for<I> S> subrange(I, S) -> subrange<I, S>; template<input_­or_­output_­iterator I, sentinel_­for<I> S> subrange(I, S, make-unsigned-like-t<iter_difference_t<I>>) -> subrange<I, S, subrange_kind::sized>; template<borrowed_­range R> subrange(R&&) -> subrange<iterator_t<R>, sentinel_t<R>, (sized_­range<R> || sized_­sentinel_­for<sentinel_t<R>, iterator_t<R>>) ? subrange_kind::sized : subrange_kind::unsized>; template<borrowed_­range R> subrange(R&&, make-unsigned-like-t<range_difference_t<R>>) -> subrange<iterator_t<R>, sentinel_t<R>, subrange_kind::sized>; }

26.5.4.2 Constructors and conversions [range.subrange.ctor]

constexpr subrange(convertible-to-non-slicing<I> auto i, S s) requires (!StoreSize);
Preconditions: [i, s) is a valid range.
Effects: Initializes begin_­ with std​::​move(i) and end_­ with s.
constexpr subrange(convertible-to-non-slicing<I> auto i, S s, make-unsigned-like-t<iter_difference_t<I>> n) requires (K == subrange_kind::sized);
Preconditions: [i, s) is a valid range, and n == to-unsigned-like(ranges​::​distance(i, s)) is true.
Effects: Initializes begin_­ with std​::​move(i) and end_­ with s.
If StoreSize is true, initializes size_­ with n.
[Note 1:
Accepting the length of the range and storing it to later return from size() enables subrange to model sized_­range even when it stores an iterator and sentinel that do not model sized_­sentinel_­for.
— end note]
template<different-from<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>);
Effects: Equivalent to:
  • If StoreSize is true, subrange(r, static_­cast<decltype(size_­)>(ranges​::​size(r))).
  • Otherwise, subrange(ranges​::​begin(r), ranges​::​end(r)).
template<different-from<subrange> PairLike> requires pair-like-convertible-from<PairLike, const I&, const S&> constexpr operator PairLike() const;
Effects: Equivalent to: return PairLike(begin_­, end_­);

26.5.4.3 Accessors [range.subrange.access]

constexpr I begin() const requires copyable<I>;
Effects: Equivalent to: return begin_­;
[[nodiscard]] constexpr I begin() requires (!copyable<I>);
Effects: Equivalent to: return std​::​move(begin_­);
constexpr S end() const;
Effects: Equivalent to: return end_­;
constexpr bool empty() const;
Effects: Equivalent to: return begin_­ == end_­;
constexpr make-unsigned-like-t<iter_difference_t<I>> size() const requires (K == subrange_kind::sized);
Effects:
  • If StoreSize is true, equivalent to: return size_­;
  • Otherwise, equivalent to: return to-unsigned-like(end_­ - begin_­);
[[nodiscard]] constexpr subrange next(iter_difference_t<I> n = 1) const & requires forward_­iterator<I>;
Effects: Equivalent to: auto tmp = *this; tmp.advance(n); return tmp;
[[nodiscard]] constexpr subrange next(iter_difference_t<I> n = 1) &&;
Effects: Equivalent to: advance(n); return std::move(*this);
[[nodiscard]] constexpr subrange prev(iter_difference_t<I> n = 1) const requires bidirectional_­iterator<I>;
Effects: Equivalent to: auto tmp = *this; tmp.advance(-n); return tmp;
constexpr subrange& advance(iter_difference_t<I> n);
Effects: Equivalent to: if constexpr (bidirectional_­iterator<I>) { if (n < 0) { ranges::advance(begin_, n); if constexpr (StoreSize) size_ += to-unsigned-like(-n); return *this; } } auto d = n - ranges::advance(begin_, n, end_); if constexpr (StoreSize) size_ -= to-unsigned-like(d); return *this;
template<size_t N, class I, class S, subrange_kind K> requires ((N == 0 && copyable<I>) || N == 1) constexpr auto get(const subrange<I, S, K>& r); template<size_t N, class I, class S, subrange_kind K> requires (N < 2) constexpr auto get(subrange<I, S, K>&& r);
Effects: Equivalent to: if constexpr (N == 0) return r.begin(); else return r.end();

26.5.5 Dangling iterator handling [range.dangling]

The tag type dangling is used together with the template aliases borrowed_­iterator_­t and borrowed_­subrange_­t.
When an algorithm that typically returns an iterator into, or a subrange of, a range argument is called with an rvalue range argument that does not model borrowed_­range ([range.range]), the return value possibly refers to a range whose lifetime has ended.
In such cases, the tag type dangling is returned instead of an iterator or subrange.
namespace std::ranges { struct dangling { constexpr dangling() noexcept = default; constexpr dangling(auto&&...) noexcept {} }; }
[Example 1: vector<int> f(); auto result1 = ranges::find(f(), 42); // #1 static_assert(same_­as<decltype(result1), ranges::dangling>); auto vec = f(); auto result2 = ranges::find(vec, 42); // #2 static_assert(same_­as<decltype(result2), vector<int>::iterator>); auto result3 = ranges::find(ranges::subrange{vec}, 42); // #3 static_assert(same_­as<decltype(result3), vector<int>::iterator>);
The call to ranges​::​find at #1 returns ranges​::​dangling since f() is an rvalue vector; it is possible for the vector to be destroyed before a returned iterator is dereferenced.
However, the calls at #2 and #3 both return iterators since the lvalue vec and specializations of subrange model borrowed_­range.
— end example]
For a type R that models range:
  • if R models borrowed_­range, then borrowed_­iterator_­t<R> denotes iterator_­t<R>, and borrowed_­subrange_­t<R> denotes subrange<iterator_­t<R>>;
  • otherwise, both borrowed_­iterator_­t<R> and borrowed_­subrange_­t<R> denote dangling.

26.5.6 Class template elements_­of [ranges.elementsof]

Specializations of elements_­of encapsulate a range and act as a tag in overload sets to disambiguate when a range should be treated as a sequence rather than a single value.
[Example 1: template<bool YieldElements> generator<any> f(ranges::input_­range auto&& r) { if constexpr (YieldElements) co_yield ranges::elements_of(r); // yield each element of r else co_yield r; // yield r as a single value } — end example]
namespace std::ranges { template<range R, class Allocator = allocator<byte>> struct elements_of { [[no_unique_address]] R range; [[no_unique_address]] Allocator allocator = Allocator(); }; template<class R, class Allocator = allocator<byte>> elements_of(R&&, Allocator = Allocator()) -> elements_of<R&&, Allocator>; }

26.5.7 Range conversions [range.utility.conv]

26.5.7.1 General [range.utility.conv.general]

The range conversion functions construct an object (usually a container) from a range, by using a constructor taking a range, a from_­range_­t tagged constructor, or a constructor taking a pair of iterators, or by inserting each element of the range into the default-constructed object.
ranges​::​to is applied recursively, allowing the conversion of a range of ranges.
[Example 1: string_view str = "the quick brown fox"; auto words = views::split(str, ' ') | to<vector<string>>(); // words is vector<string>{"the", "quick", "brown", "fox"} — end example]
Let reservable-container be defined as follows: template<class Container> constexpr bool reservable-container = // exposition only sized_­range<Container> && requires(Container& c, range_size_t<Container> n) { c.reserve(n); { c.capacity() } -> same_­as<decltype(n)>; { c.max_size() } -> same_­as<decltype(n)>; };
Let container-insertable be defined as follows: template<class Container, class Ref> constexpr bool container-insertable = // exposition only requires(Container& c, Ref&& ref) { requires (requires { c.push_back(std::forward<Ref>(ref)); } || requires { c.insert(c.end(), std::forward<Ref>(ref)); }); };
Let container-inserter be defined as follows: template<class Ref, class Container> constexpr auto container-inserter(Container& c) { // exposition only if constexpr (requires { c.push_back(declval<Ref>()); }) return back_inserter(c); else return inserter(c, c.end()); }

26.5.7.2 ranges​::​to [range.utility.conv.to]

template<class C, input_­range R, class... Args> requires (!view<C>) constexpr C to(R&& r, Args&&... args);
Returns: An object of type C constructed from the elements of r in the following manner:
template<template<class...> class C, input_­range R, class... Args> constexpr auto to(R&& r, Args&&... args);
Let input-iterator be an exposition-only type: struct input-iterator { // exposition only using iterator_category = input_iterator_tag; using value_type = range_value_t<R>; using difference_type = ptrdiff_t; using pointer = add_pointer_t<range_reference_t<R>>; using reference = range_reference_t<R>; reference operator*() const; pointer operator->() const; input-iterator& operator++(); input-iterator operator++(int); bool operator==(const input-iterator&) const; };
[Note 1:
input-iterator meets the syntactic requirements of Cpp17InputIterator.
— end note]
Let DEDUCE_­EXPR be defined as follows:
  • C(declval<R>(), declval<Args>()...) if that is a valid expression,
  • otherwise, C(from_­range, declval<R>(), declval<Args>()...) if that is a valid expression,
  • otherwise, C(declval<input-iterator>(), declval<input-iterator>(), declval<Args>()...) if that is a valid expression,
  • otherwise, the program is ill-formed.
Returns: to<decltype(DEDUCE_­EXPR)>(std​::​forward<R>(r), std​::​forward<Args>(args)...).

26.5.7.3 ranges​::​to adaptors [range.utility.conv.adaptors]

template<class C, class... Args> requires (!view<C>) constexpr auto to(Args&&... args); template<template<class...> class C, class... Args> constexpr auto to(Args&&... args);
Returns: A range adaptor closure object ([range.adaptor.object]) f that is a perfect forwarding call wrapper ([func.require]) with the following properties:
  • It has no target object.
  • Its bound argument entities bound_­args consist of objects of types decay_­t<Args>... direct-non-list-initialized with std​::​forward<Args>(args)..., respectively.
  • Its call pattern is to<C>(r, bound_­args...), where r is the argument used in a function call expression of f.