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

30 KiB
Raw Permalink Blame History

[range.join.with]

25 Ranges library [ranges]

25.7 Range adaptors [range.adaptors]

25.7.15 Join with view [range.join.with]

25.7.15.1 Overview [range.join.with.overview]

1

#

join_with_view takes a view and a delimiter, and flattens the view, inserting every element of the delimiter in between elements of the view.

The delimiter can be a single element or a view of elements.

2

#

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

Given subexpressions E and F, the expression views::join_with(E, F) is expression-equivalent tojoin_with_view(E, F).

3

#

[Example 1: vector vs = {"the", "quick", "brown", "fox"};for (char c : vs | views::join_with('-')) { cout << c;}// The above prints the-quick-brown-fox — end example]

25.7.15.2 Class template join_with_view [range.join.with.view]

🔗

namespace std::ranges {templateconcept bidirectional-common = bidirectional_range && common_range; // exposition onlytemplate<input_range V, forward_range Pattern>requires view && input_range<range_reference_t>&& view&& concatable<range_reference_t, Pattern>class join_with_view : public view_interface<join_with_view<V, Pattern>> {using InnerRng = range_reference_t; // exposition only V base_ = V(); // exposition only**non-propagating-cache<iterator_t> outer_it_; // exposition only, present only// when forward_rangenon-propagating-cache<remove_cv_t<InnerRng>> inner_; // exposition only, present only// if is_reference_v<InnerRng> is false Pattern pattern_ = Pattern(); // exposition only// [range.join.with.iterator], class template join_with_view::iteratortemplate struct iterator; // exposition only// [range.join.with.sentinel], class template join_with_view::sentineltemplate struct sentinel; // exposition onlypublic: join_with_view()requires default_initializable && default_initializable = default; constexpr explicit join_with_view(V base, Pattern pattern); template<input_range R>requires constructible_from<V, views::all_t> &&constructible_from<Pattern, single_view<range_value_t<InnerRng>>>constexpr explicit join_with_view(R&& r, range_value_t<InnerRng> e); 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> && simple-view; return iterator<use_const>{*this, ranges::begin(base_)}; }else {outer_it_ = ranges::begin(base_); return iterator{*this}; }}constexpr auto begin() constrequires forward_range &&forward_range && is_reference_v<range_reference_t> &&input_range<range_reference_t> &&concatable<range_reference_t, const Pattern> {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 && simple-view>{*this, ranges::end(base_)}; elsereturn sentinel<simple-view && simple-view>{*this}; }constexpr auto end() constrequires forward_range && forward_range && is_reference_v<range_reference_t> &&input_range<range_reference_t> &&concatable<range_reference_t, const Pattern> {using InnerConstRng = range_reference_t; if constexpr (forward_range &&common_range && common_range)return iterator{*this, ranges::end(base_)}; elsereturn sentinel{*this}; }}; template<class R, class P> join_with_view(R&&, P&&) -> join_with_view<views::all_t, views::all_t

>; template<input_range R> join_with_view(R&&, range_value_t<range_reference_t>)-> join_with_view<views::all_t, single_view<range_value_t<range_reference_t>>>;}

🔗

constexpr explicit join_with_view(V base, Pattern pattern);

1

#

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

🔗

template<[input_range](range.refinements#concept:input_range "25.4.6Other range refinements[range.refinements]") R> requires [constructible_from](concept.constructible#concept:constructible_from "18.4.11Concept constructible_­from[concept.constructible]")<V, views::all_t<R>> && [constructible_from](concept.constructible#concept:constructible_from "18.4.11Concept constructible_­from[concept.constructible]")<Pattern, single_view<range_value_t<InnerRng>>> constexpr explicit join_with_view(R&& r, range_value_t<InnerRng> e);

2

#

Effects: Initializes base_ with views::all(std::forward(r)) andpattern_ with views::single(std::move(e)).

25.7.15.3 Class template join_with_view::iterator [range.join.with.iterator]

🔗

namespace std::ranges {template<input_range V, forward_range Pattern>requires view && input_range<range_reference_t>&& view && concatable<range_reference_t, Pattern>templateclass join_with_view<V, Pattern>::iterator {using Parent = maybe-const<Const, join_with_view>; // exposition onlyusing Base = maybe-const<Const, V>; // exposition onlyusing InnerBase = range_reference_t<Base>; // exposition onlyusing PatternBase = maybe-const<Const, Pattern>; // exposition onlyusing OuterIter = iterator_t<Base>; // exposition onlyusing InnerIter = iterator_t<InnerBase>; // exposition onlyusing PatternIter = iterator_t<PatternBase>; // exposition onlystatic constexpr bool ref-is-glvalue = is_reference_v<InnerBase>; // exposition onlyParent parent_ = nullptr; // exposition only**OuterIter outer_it_ = OuterIter(); // exposition only, present only// if Base models forward_range variant<PatternIter, InnerIter> inner_it_; // exposition onlyconstexpr iterator(Parent& parent, OuterIter outer)requires forward_range<Base>; // exposition onlyconstexpr explicit iterator(Parent& parent)requires (forward_range<Base>); // exposition onlyconstexpr OuterIter& outer(); // exposition onlyconstexpr const OuterIter& outer() const; // exposition onlyconstexpr auto& update-inner(); // exposition onlyconstexpr auto& get-inner(); // exposition onlyconstexpr void satisfy(); // exposition onlypublic:using iterator_concept = see below; using iterator_category = see below; // not always presentusing value_type = see below; using difference_type = see below; iterator() = default; constexpr iterator(iterator i)requires Const && convertible_to<iterator_t, OuterIter> &&convertible_to<iterator_t<InnerRng>, InnerIter> &&convertible_to<iterator_t, PatternIter>; constexpr decltype(auto) operator() const; constexpr iterator& operator++(); constexpr void operator++(int); constexpr iterator operator++(int)requires ref-is-glvalue && forward_iterator<OuterIter> &&forward_iterator<InnerIter>; constexpr iterator& operator--()requires ref-is-glvalue && bidirectional_range<Base> &&bidirectional-common<InnerBase> && bidirectional-common<PatternBase>; constexpr iterator operator--(int)requires ref-is-glvalue && bidirectional_range<Base> &&bidirectional-common<InnerBase> && bidirectional-common<PatternBase>; friend constexpr bool operator==(const iterator& x, const iterator& y)requires ref-is-glvalue && forward_range<Base> &&equality_comparable<InnerIter>; friend constexpr decltype(auto) iter_move(const iterator& x) {using rvalue_reference = common_reference_t< iter_rvalue_reference_t<InnerIter>, iter_rvalue_reference_t<PatternIter>>; return visit<rvalue_reference>(ranges::iter_move, x.inner_it_); }friend constexpr void iter_swap(const iterator& x, const iterator& y)requires indirectly_swappable<InnerIter, PatternIter> { visit(ranges::iter_swap, x.inner_it_, y.inner_it_); }};}

1

#

iterator::iterator_concept is defined as follows:

  • (1.1)

    If ref-is-glvalue is true,Base models bidirectional_range, andInnerBase and PatternBase each model bidirectional-common, then iterator_concept denotes bidirectional_iterator_tag.

  • (1.2)

    Otherwise, if ref-is-glvalue is true andBase and InnerBase 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, andBase and InnerBase each model forward_range.

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

  • (2.1)

    Let OUTERC denoteiterator_traits<OuterIter>::iterator_category, let INNERC denoteiterator_traits<InnerIter>::iterator_category, and let PATTERNC denoteiterator_traits<PatternIter>::iterator_category.

  • (2.2)

    Ifis_reference_v<common_reference_t<iter_reference_t<InnerIter>, iter_reference_t<PatternIter>>> is false,iterator_category denotes input_iterator_tag.

  • (2.3)

    Otherwise, if OUTERC, INNERC, and PATTERNC each model derived_from<bidirectional_iterator_tag> and InnerBase and PatternBase each model common_range,iterator_category denotes bidirectional_iterator_tag.

  • (2.4)

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

  • (2.5)

    Otherwise, iterator_category denotes input_iterator_tag.

3

#

iterator::value_type denotes the type:common_type_t<iter_value_t<InnerIter>, iter_value_t<PatternIter>>

4

#

iterator::difference_type denotes the type:common_type_t< iter_difference_t<OuterIter>, iter_difference_t<InnerIter>, iter_difference_t<PatternIter>>

🔗

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

5

#

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

🔗

constexpr auto& update-inner();

6

#

Effects: Equivalent to:if constexpr (ref-is-glvalue)return as-lvalue(*outer());elsereturn parent_->inner_.emplace-deref(outer());

🔗

constexpr auto& get-inner();

7

#

Effects: Equivalent to:if constexpr (ref-is-glvalue)return as-lvalue(*outer());elsereturn **parent_*->inner_;

🔗

constexpr void satisfy();

8

#

Effects: Equivalent to:while (true) {if (inner_it_.index() == 0) {if (std::get<0>(inner_it_) != ranges::end(parent_->pattern_))break; inner_it_.template emplace<1>(ranges::begin(update-inner())); } else {if (std::get<1>(inner_it_) != ranges::end(get-inner()))break; if (++outer() == ranges::end(parent_->base_)) {if constexpr (ref-is-glvalue)inner_it_.template emplace<0>(); break; }inner_it_.template emplace<0>(ranges::begin(parent_->pattern_)); }}

[Note 1:

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

— end note]

🔗

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

9

#

Effects: Initializes parent_ with addressof(parent).

For the first overload, also initializesouter_it_ with std::move(outer).

Then, equivalent to:if (outer() != ranges::end(parent_->base_)) {inner_it_.template emplace<1>(ranges::begin(update-inner())); 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> && [convertible_to](concept.convertible#concept:convertible_to "18.4.4Concept convertible_­to[concept.convertible]")<iterator_t<Pattern>, PatternIter>;

10

#

Effects: Initializes outer_it_ withstd::move(i.outer_it_) andparent_ with i.parent_.

Then, equivalent to:if (i.inner_it_.index() == 0)inner_it_.template emplace<0>(std::get<0>(std::move(i.inner_it_)));elseinner_it_.template emplace<1>(std::get<1>(std::move(i.inner_it_)));

11

#

[Note 2:

Const can only be true when Base models forward_range.

— end note]

🔗

constexpr decltype(auto) operator*() const;

12

#

Effects: Equivalent to:using reference = common_reference_t<iter_reference_t<InnerIter>, iter_reference_t<PatternIter>>;return visit([](auto& it) -> reference { return *it; }, inner_it_);

🔗

constexpr iterator& operator++();

13

#

Effects: Equivalent to:visit([](auto& it){ ++it; }, inner_it_);satisfy();return *this;

🔗

constexpr void operator++(int);

14

#

Effects: Equivalent to ++*this.

🔗

constexpr iterator operator++(int) requires ref-is-glvalue && [forward_iterator](iterator.concept.forward#concept:forward_iterator "24.3.4.11Concept forward_­iterator[iterator.concept.forward]")<OuterIter> && [forward_iterator](iterator.concept.forward#concept:forward_iterator "24.3.4.11Concept forward_­iterator[iterator.concept.forward]")<InnerIter>;

15

#

Effects: Equivalent to:iterator 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-common](#concept:bidirectional-common "25.7.15.2Class template join_­with_­view[range.join.with.view]")<InnerBase> && [bidirectional-common](#concept:bidirectional-common "25.7.15.2Class template join_­with_­view[range.join.with.view]")<PatternBase>;

16

#

Effects: Equivalent to:if (outer_it_ == ranges::end(parent_->base_)) {auto&& inner = *--outer_it_; inner_it_.template emplace<1>(ranges::end(inner));}while (true) {if (inner_it_.index() == 0) {auto& it = std::get<0>(inner_it_); if (it == ranges::begin(parent_->pattern_)) {auto&& inner = --outer_it_; inner_it_.template emplace<1>(ranges::end(inner)); } else {break; }} else {auto& it = std::get<1>(inner_it_); auto&& inner = **outer_it_; if (it == ranges::begin(inner)) {inner_it_.template emplace<0>(ranges::end(parent_->pattern_)); } else {break; }}} visit([](auto& it){ --it; }, inner_it_);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-common](#concept:bidirectional-common "25.7.15.2Class template join_­with_­view[range.join.with.view]")<InnerBase> && [bidirectional-common](#concept:bidirectional-common "25.7.15.2Class template join_­with_­view[range.join.with.view]")<PatternBase>;

17

#

Effects: Equivalent to:iterator 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]")<InnerIter>;

18

#

Effects: Equivalent to:return x.outer_it_ == y.outer_it_ && x.inner_it_ == y.inner_it_;

25.7.15.4 Class template join_with_view::sentinel [range.join.with.sentinel]

🔗

namespace std::ranges {template<input_range V, forward_range Pattern>requires view && input_range<range_reference_t>&& view && concatable<range_reference_t, Pattern>templateclass join_with_view<V, Pattern>::sentinel {using Parent = maybe-const<Const, join_with_view>; // exposition onlyusing Base = maybe-const<Const, V>; // exposition only sentinel_t<Base> end_ = sentinel_t<Base>(); // exposition onlyconstexpr explicit sentinel(Parent& parent); // exposition onlypublic:sentinel() = default; 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_;