Files
2025-10-25 03:02:53 +03:00

41 KiB
Raw Permalink Blame History

[range.utility]

25 Ranges library [ranges]

25.5 Range utilities [range.utility]

25.5.1 General [range.utility.general]

1

#

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

25.5.2 Helper concepts [range.utility.helpers]

1

#

Many of the types in [range.utility] are specified in terms of the following exposition-only concepts:templateconcept simple-view = // exposition onlyview && range &&same_as<iterator_t, iterator_t> &&same_as<sentinel_t, sentinel_t>;

templateconcept has-arrow = // exposition onlyinput_iterator && (is_pointer_v || requires(const I i) { i.operator->(); });

template<class T, class U>concept different-from = // exposition onlysame_as<remove_cvref_t, remove_cvref_t>;

templateconcept range-with-movable-references = // exposition onlyinput_range && move_constructible<range_reference_t> &&move_constructible<range_rvalue_reference_t>;

25.5.3 View interface [view.interface]

25.5.3.1 General [view.interface.general]

1

#

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 {templaterequires is_class_v && same_as<D, remove_cv_t>class view_interface {private:constexpr D& derived() noexcept { // exposition onlyreturn static_cast<D&>(*this); }constexpr const D& derived() const noexcept { // exposition onlyreturn static_cast<const D&>(*this); }public:constexpr bool empty() requires sized_range || forward_range {if constexpr (sized_range)return ranges::size(derived()) == 0; elsereturn ranges::begin(derived()) == ranges::end(derived()); }constexpr bool empty() const requires sized_range || forward_range {if constexpr (sized_range)return ranges::size(derived()) == 0; elsereturn ranges::begin(derived()) == ranges::end(derived()); }constexpr auto cbegin() requires input_range {return ranges::cbegin(derived()); }constexpr auto cbegin() const requires input_range {return ranges::cbegin(derived()); }constexpr auto cend() requires input_range {return ranges::cend(derived()); }constexpr auto cend() const requires input_range {return ranges::cend(derived()); }constexpr explicit operator bool()requires requires { ranges::empty(derived()); } {return !ranges::empty(derived()); }constexpr explicit operator bool() constrequires requires { ranges::empty(derived()); } {return !ranges::empty(derived()); }constexpr auto data() requires contiguous_iterator<iterator_t> {return to_address(ranges::begin(derived())); }constexpr auto data() constrequires range && contiguous_iterator<iterator_t> {return to_address(ranges::begin(derived())); }constexpr auto size() requires forward_range &&sized_sentinel_for<sentinel_t, iterator_t> {return to-unsigned-like(ranges::end(derived()) - ranges::begin(derived())); }constexpr auto size() const requires forward_range &&sized_sentinel_for<sentinel_t, iterator_t> {return to-unsigned-like(ranges::end(derived()) - ranges::begin(derived())); }constexpr decltype(auto) front() requires forward_range; constexpr decltype(auto) front() const requires forward_range; constexpr decltype(auto) back() requires bidirectional_range && common_range; constexpr decltype(auto) back() constrequires bidirectional_range && common_range; template<random_access_range R = D>constexpr decltype(auto) operator[](range_difference_t n) {return ranges::begin(derived())[n]; }template<random_access_range R = const D>constexpr decltype(auto) operator[](range_difference_t n) const {return ranges::begin(derived())[n]; }};}

2

#

The template parameter D for view_interface may be an incomplete type.

Before any member of the resulting specialization ofview_interface other than special member functions is referenced, D shall be complete, and model both derived_from<view_interface> and view.

25.5.3.2 Members [view.interface.members]

🔗

constexpr decltype(auto) front() requires [forward_range](range.refinements#concept:forward_range "25.4.6Other range refinements[range.refinements]")<D>; constexpr decltype(auto) front() const requires [forward_range](range.refinements#concept:forward_range "25.4.6Other range refinements[range.refinements]")<const D>;

1

#

Hardened preconditions: !empty() is true.

2

#

Effects: Equivalent to: return *ranges::begin(derived());

🔗

constexpr decltype(auto) back() requires [bidirectional_range](range.refinements#concept:bidirectional_range "25.4.6Other range refinements[range.refinements]")<D> && [common_range](range.refinements#concept:common_range "25.4.6Other range refinements[range.refinements]")<D>; constexpr decltype(auto) back() const requires [bidirectional_range](range.refinements#concept:bidirectional_range "25.4.6Other range refinements[range.refinements]")<const D> && [common_range](range.refinements#concept:common_range "25.4.6Other range refinements[range.refinements]")<const D>;

3

#

Hardened preconditions: !empty() is true.

4

#

Effects: Equivalent to: return *ranges::prev(ranges::end(derived()));

25.5.4 Sub-ranges [range.subrange]

25.5.4.1 General [range.subrange.general]

1

#

The subrange class template combines together an iterator and a sentinel into a single object that models theview concept.

Additionally, it models thesized_range concept when the final template parameter issubrange_kind::sized.

🔗

namespace std::ranges {template<class From, class To>concept uses-nonqualification-pointer-conversion = // exposition only is_pointer_v && is_pointer_v &&convertible_to<remove_pointer_t()[], remove_pointer_t()[]>; template<class From, class To>concept convertible-to-non-slicing = // exposition onlyconvertible_to<From, To> &&uses-nonqualification-pointer-conversion<decay_t, decay_t>; template<class T, class U, class V>concept pair-like-convertible-from = // exposition onlyrange && !is_reference_v && pair-like &&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 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> size_ = 0; // exposition only; present only// if StoreSize is truepublic: subrange() requires default_initializable = default; constexpr subrange(convertible-to-non-slicing auto i, S s) requires (!StoreSize); constexpr subrange(convertible-to-non-slicing auto i, S s, make-unsigned-like-t<iter_difference_t> n)requires (K == subrange_kind::sized); template<different-from R>requires borrowed_range &&convertible-to-non-slicing<iterator_t, I> &&convertible_to<sentinel_t, S>constexpr subrange(R&& r) requires (!StoreSize || sized_range); template<borrowed_range R>requires convertible-to-non-slicing<iterator_t, I> &&convertible_to<sentinel_t, S>constexpr subrange(R&& r, make-unsigned-like-t<iter_difference_t> n)requires (K == subrange_kind::sized): subrange{ranges::begin(r), ranges::end(r), n} {}template<different-from PairLike>requires pair-like-convertible-from<PairLike, const I&, const S&>constexpr operator PairLike() const; constexpr I begin() const requires copyable; constexpr I begin() requires (copyable); constexpr S end() const; constexpr bool empty() const; constexpr make-unsigned-like-t<iter_difference_t> size() constrequires (K == subrange_kind::sized); constexpr subrange next(iter_difference_t n = 1) const &requires forward_iterator; constexpr subrange next(iter_difference_t n = 1) &&; constexpr subrange prev(iter_difference_t n = 1) constrequires bidirectional_iterator; constexpr subrange& advance(iter_difference_t n); }; template<input_or_output_iterator I, sentinel_for S> subrange(I, S) -> subrange<I, S>; template<input_or_output_iterator I, sentinel_for S> subrange(I, S, make-unsigned-like-t<iter_difference_t>) -> subrange<I, S, subrange_kind::sized>; template<borrowed_range R> subrange(R&&) -> subrange<iterator_t, sentinel_t, (sized_range || sized_sentinel_for<sentinel_t, iterator_t>)? subrange_kind::sized : subrange_kind::unsized>; template<borrowed_range R> subrange(R&&, make-unsigned-like-t<range_difference_t>) -> subrange<iterator_t, sentinel_t, subrange_kind::sized>;}

25.5.4.2 Constructors and conversions [range.subrange.ctor]

🔗

constexpr subrange([convertible-to-non-slicing](#concept:convertible-to-non-slicing "25.5.4.1General[range.subrange.general]")<I> auto i, S s) requires (!StoreSize);

1

#

Preconditions: [i, s) is a valid range.

2

#

Effects: Initializes begin_ with std::move(i) and end_ withs.

🔗

constexpr subrange([convertible-to-non-slicing](#concept:convertible-to-non-slicing "25.5.4.1General[range.subrange.general]")<I> auto i, S s, make-unsigned-like-t<iter_difference_t<I>> n) requires (K == subrange_kind::sized);

3

#

Preconditions: [i, s) is a valid range, andn == to-unsigned-like(ranges::distance(i, s)) is true.

4

#

Effects: Initializes begin_ with std::move(i) and end_ withs.

If StoreSize is true, initializes size_ withn.

5

#

[Note 1:

Accepting the length of the range and storing it to later return fromsize() enables subrange to model sized_range even when it stores an iterator and sentinel that do not modelsized_sentinel_for.

— end note]

🔗

template<[different-from](#concept:different-from "25.5.2Helper concepts[range.utility.helpers]")<subrange> R> requires [borrowed_range](range.range#concept:borrowed_range "25.4.2Ranges[range.range]")<R> && [convertible-to-non-slicing](#concept:convertible-to-non-slicing "25.5.4.1General[range.subrange.general]")<iterator_t<R>, I> && [convertible_to](concept.convertible#concept:convertible_to "18.4.4Concept convertible_­to[concept.convertible]")<sentinel_t<R>, S> constexpr subrange(R&& r) requires (!StoreSize || [sized_range](range.sized#concept:sized_range "25.4.4Sized ranges[range.sized]")<R>);

6

#

Effects: Equivalent to:

  • (6.1)

    If StoreSize is true,subrange(r, static_cast<decltype(size_)>(ranges::size(r))).

  • (6.2)

    Otherwise, subrange(ranges::begin(r), ranges::end(r)).

🔗

template<[different-from](#concept:different-from "25.5.2Helper concepts[range.utility.helpers]")<subrange> PairLike> requires [pair-like-convertible-from](#concept:pair-like-convertible-from "25.5.4.1General[range.subrange.general]")<PairLike, const I&, const S&> constexpr operator PairLike() const;

7

#

Effects: Equivalent to: return PairLike(begin_, end_);

25.5.4.3 Accessors [range.subrange.access]

🔗

constexpr I begin() const requires [copyable](concepts.object#concept:copyable "18.6Object concepts[concepts.object]")<I>;

1

#

Effects: Equivalent to: return begin_;

🔗

constexpr I begin() requires (![copyable](concepts.object#concept:copyable "18.6Object concepts[concepts.object]")<I>);

2

#

Effects: Equivalent to: return std::move(begin_);

🔗

constexpr S end() const;

3

#

Effects: Equivalent to: return end_;

🔗

constexpr bool empty() const;

4

#

Effects: Equivalent to: return begin_ == end_;

🔗

constexpr make-unsigned-like-t<iter_difference_t<I>> size() const requires (K == subrange_kind::sized);

5

#

Effects:

If StoreSize is true, equivalent to: return size_;

Otherwise, equivalent to: return to-unsigned-like(end_ - begin_);

🔗

constexpr subrange next(iter_difference_t<I> n = 1) const & requires [forward_iterator](iterator.concept.forward#concept:forward_iterator "24.3.4.11Concept forward_­iterator[iterator.concept.forward]")<I>;

6

#

Effects: Equivalent to:auto tmp = *this; tmp.advance(n);return tmp;

🔗

constexpr subrange next(iter_difference_t<I> n = 1) &&;

7

#

Effects: Equivalent to:advance(n);return std::move(*this);

🔗

constexpr subrange prev(iter_difference_t<I> n = 1) const requires [bidirectional_iterator](iterator.concept.bidir#concept:bidirectional_iterator "24.3.4.12Concept bidirectional_­iterator[iterator.concept.bidir]")<I>;

8

#

Effects: Equivalent to:auto tmp = *this; tmp.advance(-n);return tmp;

🔗

constexpr subrange& advance(iter_difference_t<I> n);

9

#

Effects: Equivalent to:if constexpr (bidirectional_iterator) {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](concepts.object#concept:copyable "18.6Object concepts[concepts.object]")<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);

10

#

Effects: Equivalent to:if constexpr (N == 0)return r.begin();elsereturn r.end();

25.5.5 Dangling iterator handling [range.dangling]

1

#

The type dangling is used together with the template aliasesborrowed_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 type dangling is returned instead of an iterator or subrange.

🔗

namespace std::ranges {struct dangling {constexpr dangling() noexcept = default; constexpr dangling(auto&&...) noexcept {}};}

2

#

[Example 1: vector f();auto result1 = ranges::find(f(), 42); // #1static_assert(same_as<decltype(result1), ranges::dangling>);auto vec = f();auto result2 = ranges::find(vec, 42); // #2static_assert(same_as<decltype(result2), vector::iterator>);auto result3 = ranges::find(ranges::subrange{vec}, 42); // #3static_assert(same_as<decltype(result3), vector::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]

3

#

For a type R that models range:

if R models borrowed_range, thenborrowed_iterator_t denotes iterator_t, andborrowed_subrange_t denotes subrange<iterator_t>;

otherwise, both borrowed_iterator_t and borrowed_subrange_t denote dangling.

25.5.6 Class template elements_of [range.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 generator f(ranges::input_range auto&& r) {if constexpr (YieldElements)co_yield ranges::elements_of(r); // yield each element of relseco_yield r; // yield r as a single value} — end example]

namespace std::ranges {template<range R, class Allocator = allocator>struct elements_of {no_unique_address R range; no_unique_address Allocator allocator = Allocator(); }; template<class R, class Allocator = allocator> elements_of(R&&, Allocator = Allocator()) -> elements_of<R&&, Allocator>;}

25.5.7 Range conversions [range.utility.conv]

25.5.7.1 General [range.utility.conv.general]

1

#

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.

2

#

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>();// words is vector{"the", "quick", "brown", "fox"} — end example]

3

#

Let reservable-container be defined as follows:templateconstexpr bool reservable-container = // exposition onlysized_range &&requires(Container& c, range_size_t n) { c.reserve(n); { c.capacity() } -> same_as<decltype(n)>; { c.max_size() } -> same_as<decltype(n)>; };

4

#

Let container-appendable be defined as follows:template<class Container, class Ref>constexpr bool container-appendable = // exposition onlyrequires(Container& c, Ref&& ref) {requires (requires { c.emplace_back(std::forward(ref)); } ||requires { c.push_back(std::forward(ref)); } ||requires { c.emplace(c.end(), std::forward(ref)); } ||requires { c.insert(c.end(), std::forward(ref)); }); };

5

#

Let container-append be defined as follows:templateconstexpr auto container-append(Container& c) { // exposition onlyreturn [&c](Ref&& ref) {if constexpr (requires { c.emplace_back(declval()); }) c.emplace_back(std::forward(ref)); else if constexpr (requires { c.push_back(declval()); }) c.push_back(std::forward(ref)); else if constexpr (requires { c.emplace(c.end(), declval()); }) c.emplace(c.end(), std::forward(ref)); else c.insert(c.end(), std::forward(ref)); };}

25.5.7.2 ranges::to [range.utility.conv.to]

🔗

template<class C, [input_range](range.refinements#concept:input_range "25.4.6Other range refinements[range.refinements]") R, class... Args> requires (![view](range.view#concept:view "25.4.5Views[range.view]")<C>) constexpr C to(R&& r, Args&&... args);

1

#

Mandates: C is a cv-unqualified class type.

2

#

Returns: An object of type C constructed from the elements of r in the following manner:

If constructible_from<C, R, Args...> is true:C(std::forward(r), std::forward(args)...)

Otherwise, ifconstructible_from<C, from_range_t, R, Args...> is true:C(from_range, std::forward(r), std::forward(args)...)

Otherwise, if + (2.1.3.1) common_range is true,

+
      [(2.1.3.2)](#conv.to-2.1.3.2)

the qualified-iditerator_traits<iterator_t>::iterator_category is valid and denotes a type that modelsderived_from<input_iterator_tag>, and

+
      [(2.1.3.3)](#conv.to-2.1.3.3)

constructible_from<C, iterator_t, sentinel_t, Args...> is true:

C(ranges::begin(r), ranges::end(r), std::forward(args)...)

Otherwise, if + (2.1.4.1) constructible_from<C, Args...> is true, and

+
      [(2.1.4.2)](#conv.to-2.1.4.2)

container-appendable<C, range_reference_t> is true:

C c(std::forward(args)...);if constexpr (approximately_sized_range && reservable-container) c.reserve(static_cast<range_size_t>(ranges::reserve_hint(r))); ranges::for_each(r, container-append(c));

Otherwise, the program is ill-formed.

  • (2.2)

    Otherwise, if input_range<range_reference_t> is true:to(ref_view(r) | views::transform([](auto&& elem) {return to<range_value_t>(std::forward<decltype(elem)>(elem));}), std::forward(args)...);

  • (2.3)

    Otherwise, the program is ill-formed.

🔗

template<template<class...> class C, [input_range](range.refinements#concept:input_range "25.4.6Other range refinements[range.refinements]") R, class... Args> constexpr auto to(R&& r, Args&&... args);

3

#

Let input-iterator be an exposition-only type:struct input-iterator { // exposition onlyusing iterator_category = input_iterator_tag; using value_type = range_value_t; using difference_type = ptrdiff_t; using pointer = add_pointer_t<range_reference_t>; using reference = range_reference_t; 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]

4

#

Let DEDUCE_EXPR be defined as follows:

C(declval(), declval()...) if that is a valid expression,

otherwise, C(from_range, declval(), declval()...) if that is a valid expression,

otherwise,C(declval<input-iterator>(), declval<input-iterator>(), declval()...) if that is a valid expression,

otherwise, the program is ill-formed.

5

#

Returns: to<decltype(DEDUCE_EXPR)>(std::forward(r), std::forward(args)...).

25.5.7.3 ranges::to adaptors [range.utility.conv.adaptors]

🔗

template<class C, class... Args> requires (![view](range.view#concept:view "25.4.5Views[range.view]")<C>) constexpr auto to(Args&&... args); template<template<class...> class C, class... Args> constexpr auto to(Args&&... args);

1

#

Mandates: For the first overload,C is a cv-unqualified class type.

2

#

Returns: A range adaptor closure object ([range.adaptor.object]) f that is a perfect forwarding call wrapper ([func.require]) with the following properties:

  • (2.1)

    It has no target object.

  • (2.2)

    Its bound argument entities bound_args consist of objects of types decay_t... direct-non-list-initialized with std::forward(args)..., respectively.

  • (2.3)

    Its call pattern is to(r, bound_args...), where r is the argument used in a function call expression of f.