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

25 KiB
Raw Permalink Blame History

[range.join]

25 Ranges library [ranges]

25.7 Range adaptors [range.adaptors]

25.7.14 Join view [range.join]

25.7.14.1 Overview [range.join.overview]

1

#

join_view flattens a view of ranges into a view.

2

#

The name views::join denotes a range adaptor object ([range.adaptor.object]).

Given a subexpression E, the expressionviews::join(E) is expression-equivalent tojoin_view<views::all_t<decltype((E))>>{E}.

3

#

[Example 1: vector ss{"hello", " ", "world", "!"};for (char ch : ss | views::join) cout << ch; // prints hello world! — end example]

25.7.14.2 Class template join_view [range.join.view]

🔗

namespace std::ranges {template<input_range V>requires view && input_range<range_reference_t>class join_view : public view_interface<join_view> {private:using InnerRng = range_reference_t; // exposition only// [range.join.iterator], class template join_view::iteratortemplatestruct iterator; // exposition only// [range.join.sentinel], class template join_view::sentineltemplatestruct sentinel; // exposition only V base_ = V(); // exposition only**non-propagating-cache<iterator_t> outer_; // exposition only, present only// when forward_rangenon-propagating-cache<remove_cv_t<InnerRng>> inner_; // exposition only, present only// if is_reference_v<InnerRng> is falsepublic: join_view() requires default_initializable = default; constexpr explicit join_view(V base); constexpr V base() const & requires copy_constructible { return base_; }constexpr V base() && { return std::move(base_); }constexpr auto begin() {if constexpr (forward_range) {constexpr bool use_const = simple-view && is_reference_v<InnerRng>; return iterator<use_const>{*this, ranges::begin(base_)}; } else {outer_ = ranges::begin(base_); return iterator{*this}; }}constexpr auto begin() constrequires forward_range && is_reference_v<range_reference_t> &&input_range<range_reference_t>{ return iterator{*this, ranges::begin(base_)}; }constexpr auto end() {if constexpr (forward_range && is_reference_v<InnerRng> && forward_range<InnerRng> &&common_range && common_range<InnerRng>)return iterator<simple-view>{*this, ranges::end(base_)}; elsereturn sentinel<simple-view>{*this}; }constexpr auto end() constrequires forward_range && is_reference_v<range_reference_t> &&input_range<range_reference_t> {if constexpr (forward_range<range_reference_t> &&common_range &&common_range<range_reference_t>)return iterator{*this, ranges::end(base_)}; elsereturn sentinel{*this}; }}; templateexplicit join_view(R&&) -> join_view<views::all_t>;}

🔗

constexpr explicit join_view(V base);

1

#

Effects: Initializes base_ with std::move(base).

25.7.14.3 Class template join_view::iterator [range.join.iterator]

🔗

namespace std::ranges {template<input_range V>requires view && input_range<range_reference_t>templatestruct join_view::iterator {private:using Parent = maybe-const<Const, join_view>; // exposition onlyusing Base = maybe-const<Const, V>; // exposition onlyusing OuterIter = iterator_t<Base>; // exposition onlyusing InnerIter = iterator_t<range_reference_t<Base>>; // exposition onlystatic constexpr bool ref-is-glvalue = // exposition only is_reference_v<range_reference_t<Base>>; OuterIter outer_ = OuterIter(); // exposition only, present only// if Base models forward_range optional<InnerIter> inner_; // exposition onlyParent parent_ = nullptr; // exposition onlyconstexpr void satisfy(); // exposition onlyconstexpr OuterIter& outer(); // exposition onlyconstexpr const OuterIter& outer() const; // exposition onlyconstexpr iterator(Parent& parent, OuterIter outer)requires forward_range<Base>; // exposition onlyconstexpr explicit iterator(Parent& parent)requires (forward_range<Base>); // exposition onlypublic:using iterator_concept = see below; using iterator_category = see below; // not always presentusing value_type = range_value_t<range_reference_t<Base>>; using difference_type = see below; iterator() = default; constexpr iterator(iterator i)requires Const &&convertible_to<iterator_t, OuterIter> &&convertible_to<iterator_t<InnerRng>, InnerIter>; constexpr decltype(auto) operator() const { return **inner_; }constexpr InnerIter operator->() constrequires has-arrow<InnerIter> && copyable<InnerIter>; constexpr iterator& operator++(); constexpr void operator++(int); constexpr iterator operator++(int)requires ref-is-glvalue && forward_range<Base> &&forward_range<range_reference_t<Base>>; constexpr iterator& operator--()requires ref-is-glvalue && bidirectional_range<Base> &&bidirectional_range<range_reference_t<Base>> &&common_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>>; friend constexpr bool operator==(const iterator& x, const iterator& y)requires ref-is-glvalue && forward_range<Base> &&equality_comparable<iterator_t<range_reference_t<Base>>>; friend constexpr decltype(auto) iter_move(const iterator& i)noexcept(noexcept(ranges::iter_move(*i.inner_))) {return ranges::iter_move(*i.inner_); }friend constexpr void iter_swap(const iterator& x, const iterator& y)noexcept(noexcept(ranges::iter_swap(*x.inner_, *y.inner_)))requires indirectly_swappable<InnerIter>; };}

1

#

iterator::iterator_concept is defined as follows:

  • (1.1)

    If ref-is-glvalue is true, Base models bidirectional_range, and range_reference_t<Base> models both bidirectional_range and common_range, then iterator_concept denotes bidirectional_iterator_tag.

  • (1.2)

    Otherwise, if ref-is-glvalue is true and Base and range_reference_t<Base> each model forward_range, then iterator_concept denotes forward_iterator_tag.

  • (1.3)

    Otherwise, iterator_concept denotes input_iterator_tag.

2

#

The member typedef-name iterator_category is defined if and only if ref-is-glvalue is true,Base models forward_range, andrange_reference_t<Base> models forward_range.

In that case,iterator::iterator_category is defined as follows:

  • (2.1)

    Let OUTERC denote iterator_traits<iterator_t<Base>>::iterator_category, and let INNERC denote iterator_traits<iterator_t<range_reference_t<Base>>>::iterator_category.

  • (2.2)

    If OUTERC and INNERC each model derived_from<bidirectional_iterator_tag> and range_reference_t<Base> models common_range, iterator_category denotes bidirectional_iterator_tag.

  • (2.3)

    Otherwise, if OUTERC and INNERC each model derived_from<forward_iterator_tag>, iterator_category denotes forward_iterator_tag.

  • (2.4)

    Otherwise, iterator_category denotes input_iterator_tag.

3

#

iterator::difference_type denotes the type:common_type_t< range_difference_t<Base>, range_difference_t<range_reference_t<Base>>>

4

#

join_view iterators use the satisfy function to skip over empty inner ranges.

🔗

constexpr OuterIter& outer(); constexpr const OuterIter& outer() const;

5

#

Returns: outer_ if Base models forward_range; otherwise, **parent_*->outer_.

🔗

constexpr void satisfy();

6

#

Effects: Equivalent to:auto update_inner = [this](const iterator_t<Base>& x) -> auto&& {if constexpr (ref-is-glvalue) // *x is a referencereturn *x; elsereturn parent_->inner_.emplace-deref(x);};

for (; outer() != ranges::end(parent_->base_); ++outer()) {auto&& inner = update_inner(outer()); inner_ = ranges::begin(inner); if (*inner_ != ranges::end(inner))return;}if constexpr (ref-is-glvalue)inner_.reset();

🔗

constexpr iterator(Parent& parent, OuterIter outer) requires [forward_range](range.refinements#concept:forward_range "25.4.6Other range refinements[range.refinements]")<Base>;

7

#

Effects: Initializes outer_ with std::move(outer) andparent_ with addressof(parent); then calls satisfy().

🔗

constexpr explicit iterator(Parent& parent) requires (![forward_range](range.refinements#concept:forward_range "25.4.6Other range refinements[range.refinements]")<Base>);

8

#

Effects: Initializes parent_ with addressof(parent); then calls satisfy().

🔗

constexpr iterator(iterator<!Const> i) requires Const && [convertible_to](concept.convertible#concept:convertible_to "18.4.4Concept convertible_­to[concept.convertible]")<iterator_t<V>, OuterIter> && [convertible_to](concept.convertible#concept:convertible_to "18.4.4Concept convertible_­to[concept.convertible]")<iterator_t<InnerRng>, InnerIter>;

9

#

Effects: Initializes outer_ with std::move(i.outer_),inner_ with std::move(i.inner_), andparent_ with i.parent_.

10

#

[Note 1:

Const can only be true when Base models forward_range.

— end note]

🔗

constexpr InnerIter operator->() const requires [has-arrow](range.utility.helpers#concept:has-arrow "25.5.2Helper concepts[range.utility.helpers]")<InnerIter> && [copyable](concepts.object#concept:copyable "18.6Object concepts[concepts.object]")<InnerIter>;

11

#

Effects: Equivalent to: return **inner_*;

🔗

constexpr iterator& operator++();

12

#

Let inner-range be:

  • (12.1)

    If ref-is-glvalue is true, *outer().

  • (12.2)

    Otherwise, **parent_*->inner_.

13

#

Effects: Equivalent to:if (++*inner_ == ranges::end(as-lvalue(inner-range))) {++outer(); satisfy();}return *this;

🔗

constexpr void operator++(int);

14

#

Effects: Equivalent to: ++*this.

🔗

constexpr iterator operator++(int) requires ref-is-glvalue && [forward_range](range.refinements#concept:forward_range "25.4.6Other range refinements[range.refinements]")<Base> && [forward_range](range.refinements#concept:forward_range "25.4.6Other range refinements[range.refinements]")<range_reference_t<Base>>;

15

#

Effects: Equivalent to:auto tmp = *this;++*this;return tmp;

🔗

constexpr iterator& operator--() requires ref-is-glvalue && [bidirectional_range](range.refinements#concept:bidirectional_range "25.4.6Other range refinements[range.refinements]")<Base> && [bidirectional_range](range.refinements#concept:bidirectional_range "25.4.6Other range refinements[range.refinements]")<range_reference_t<Base>> && [common_range](range.refinements#concept:common_range "25.4.6Other range refinements[range.refinements]")<range_reference_t<Base>>;

16

#

Effects: Equivalent to:if (outer_ == ranges::end(parent_->base_))inner_ = ranges::end(as-lvalue(--outer_));while (*inner_ == ranges::begin(as-lvalue(**outer_)))*inner_ = ranges::end(as-lvalue(--outer_));--**inner_;return *this;

🔗

constexpr iterator operator--(int) requires ref-is-glvalue && [bidirectional_range](range.refinements#concept:bidirectional_range "25.4.6Other range refinements[range.refinements]")<Base> && [bidirectional_range](range.refinements#concept:bidirectional_range "25.4.6Other range refinements[range.refinements]")<range_reference_t<Base>> && [common_range](range.refinements#concept:common_range "25.4.6Other range refinements[range.refinements]")<range_reference_t<Base>>;

17

#

Effects: Equivalent to:auto tmp = *this;--*this;return tmp;

🔗

friend constexpr bool operator==(const iterator& x, const iterator& y) requires ref-is-glvalue && [forward_range](range.refinements#concept:forward_range "25.4.6Other range refinements[range.refinements]")<Base> && [equality_comparable](concept.equalitycomparable#concept:equality_comparable "18.5.4Concept equality_­comparable[concept.equalitycomparable]")<iterator_t<range_reference_t<Base>>>;

18

#

Effects: Equivalent to:return x.outer_ == y.outer_ && x.inner_ == y.inner_;

🔗

friend constexpr void iter_swap(const iterator& x, const iterator& y) noexcept(noexcept(ranges::iter_swap(*x.inner_, *y.inner_))) requires [indirectly_swappable](alg.req.ind.swap#concept:indirectly_swappable "24.3.7.4Concept indirectly_­swappable[alg.req.ind.swap]")<InnerIter>;

19

#

Effects: Equivalent to: ranges::iter_swap(*x.inner_, *y.inner_);

25.7.14.4 Class template join_view::sentinel [range.join.sentinel]

🔗

namespace std::ranges {template<input_range V>requires view && input_range<range_reference_t>templatestruct join_view::sentinel {private:using Parent = maybe-const<Const, join_view>; // exposition onlyusing Base = maybe-const<Const, V>; // exposition only sentinel_t<Base> end_ = sentinel_t<Base>(); // exposition onlypublic:sentinel() = default; constexpr explicit sentinel(Parent& parent); constexpr sentinel(sentinel s)requires Const && convertible_to<sentinel_t, sentinel_t<Base>>; templaterequires sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>>friend constexpr bool operator==(const iterator& x, const sentinel& y); };}

🔗

constexpr explicit sentinel(Parent& parent);

1

#

Effects: Initializes end_ with ranges::end(parent.base_).

🔗

constexpr sentinel(sentinel<!Const> s) requires Const && [convertible_to](concept.convertible#concept:convertible_to "18.4.4Concept convertible_­to[concept.convertible]")<sentinel_t<V>, sentinel_t<Base>>;

2

#

Effects: Initializes end_ with std::move(s.end_).

🔗

template<bool OtherConst> requires [sentinel_for](iterator.concept.sentinel#concept:sentinel_for "24.3.4.7Concept sentinel_­for[iterator.concept.sentinel]")<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>> friend constexpr bool operator==(const iterator<OtherConst>& x, const sentinel& y);

3

#

Effects: Equivalent to: return x.outer() == y.end_;