[range.join.with] (original) (raw)
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]
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.
Given subexpressions E and F, the expression views::join_with(E, F) is expression-equivalent tojoin_with_view(E, F).
[Example 1: vector<string> vs = {"the", "quick", "brown", "fox"};for (char c : vs | views::join_with('-')) { cout << c;} — _end example_]
25.7.15.2 Class template join_with_view [range.join.with.view]
namespace std::ranges { template<class R> concept bidirectional-common = bidirectional_range<R> && common_range<R>; template<input_range V, forward_range Pattern> requires view<V> && input_range<range_reference_t<V>> && view<Pattern> && concatable<range_reference_t<V>, Pattern> class join_with_view : public view_interface<join_with_view<V, Pattern>> { using InnerRng = range_reference_t<V>; V base_ = V(); non-propagating-cache<iterator_t<V>> outer_it_; non-propagating-cache<remove_cv_t<_InnerRng_>> inner_; Pattern pattern_ = Pattern(); template<bool Const> struct iterator; template<bool Const> struct sentinel; public: join_with_view() requires default_initializable<V> && default_initializable<Pattern> = default;constexpr explicit join_with_view(V base, Pattern pattern);template<input_range R> requires constructible_from<V, views::all_t<R>> && 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<V> { return base_; } constexpr V base() && { return std::move(base_); } constexpr auto begin() { if constexpr (forward_range<V>) { constexpr bool use_const = simple-view<V> && is_reference_v<_InnerRng_> && simple-view<Pattern>;return iterator<use_const>{*this, ranges::begin(base_)};} else { outer_it_ = ranges::begin(base_);return iterator<false>{*this};} } constexpr auto begin() const requires forward_range<const V> && forward_range<const Pattern> && is_reference_v<range_reference_t<const V>> && input_range<range_reference_t<const V>> && concatable<range_reference_t<const V>, const Pattern> { return iterator<true>{*this, ranges::begin(base_)};} constexpr auto end() { if constexpr (forward_range<V> && is_reference_v<_InnerRng_> && forward_range<_InnerRng_> && common_range<V> && common_range<_InnerRng_>) return iterator<simple-view<V> && simple-view<Pattern>>{*this, ranges::end(base_)};else return sentinel<simple-view<V> && simple-view<Pattern>>{*this};} constexpr auto end() const requires forward_range<const V> && forward_range<const Pattern> && is_reference_v<range_reference_t<const V>> && input_range<range_reference_t<const V>> && concatable<range_reference_t<const V>, const Pattern> { using InnerConstRng = range_reference_t<const V>;if constexpr (forward_range<InnerConstRng> && common_range<const V> && common_range<InnerConstRng>) return iterator<true>{*this, ranges::end(base_)};else return sentinel<true>{*this};} };template<class R, class P> join_with_view(R&&, P&&) -> join_with_view<views::all_t<R>, views::all_t<P>>;template<input_range R> join_with_view(R&&, range_value_t<range_reference_t<R>>) -> join_with_view<views::all_t<R>, single_view<range_value_t<range_reference_t<R>>>>;}
constexpr explicit join_with_view(V base, Pattern pattern);
Effects: Initializes base_ with std::move(base) and_pattern__ with std::move(pattern).
Effects: Initializes base_ with views::all(std::forward<R>(r)) and_pattern__ 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<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 { using Parent = maybe-const<Const, join_with_view>; using Base = maybe-const<Const, V>; using InnerBase = range_reference_t<_Base_>; using PatternBase = maybe-const<Const, Pattern>; using OuterIter = iterator_t<_Base_>; using InnerIter = iterator_t<_InnerBase_>; using PatternIter = iterator_t<_PatternBase_>; static constexpr bool ref-is-glvalue = is_reference_v<_InnerBase_>; Parent* parent_ = nullptr; OuterIter outer_it_ = OuterIter(); variant<_PatternIter_, _InnerIter_> inner_it_; constexpr iterator(Parent& parent, OuterIter outer) requires forward_range<_Base_>; constexpr explicit iterator(Parent& parent) requires (<_Base_>); constexpr OuterIter& outer(); constexpr const OuterIter& outer() const; constexpr auto& update-inner(); constexpr auto& get-inner(); constexpr void satisfy(); public: using iterator_concept = see below;using iterator_category = see below; using value_type = see below;using difference_type = see below;iterator() = default;constexpr iterator(iterator<!Const> i) requires Const && convertible_to<iterator_t<V>, _OuterIter_> && convertible_to<iterator_t<_InnerRng_>, _InnerIter_> && convertible_to<iterator_t<Pattern>, _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_);} };}
_iterator_::iterator_concept is defined as follows:
- If ref-is-glvalue is true,Base models bidirectional_range, and_InnerBase_ and _PatternBase_each model bidirectional-common, then iterator_concept denotes bidirectional_iterator_tag.
- Otherwise, if ref-is-glvalue is true and_Base_ and InnerBase each model forward_range, then iterator_concept denotes forward_iterator_tag.
- Otherwise, iterator_concept denotes input_iterator_tag.
The member typedef-name iterator_category is defined if and only if ref-is-glvalue is true, and_Base_ and InnerBase each model forward_range.
In that case,_iterator_::iterator_category is defined as follows:
- Let OUTERC denoteiterator_traits<_OuterIter_>::iterator_category, let INNERC denoteiterator_traits<_InnerIter_>::iterator_category, and let PATTERNC denoteiterator_traits<_PatternIter_>::iterator_category.
- Ifis_reference_v<common_reference_t<iter_reference_t<_InnerIter_>, iter_reference_t<_PatternIter_>>> is false,iterator_category denotes input_iterator_tag.
- 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.
- Otherwise, if OUTERC, INNERC, and _PATTERNC_each model derived_from<forward_iterator_tag>,iterator_category denotes forward_iterator_tag.
- Otherwise, iterator_category denotes input_iterator_tag.
_iterator_::value_type denotes the type:common_type_t<iter_value_t<_InnerIter_>, iter_value_t<_PatternIter_>>
_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;
Returns: outer_it_ if Base models forward_range; otherwise, *_parent__->outer_it_.
constexpr auto& _update-inner_();
Effects: Equivalent to:if constexpr (ref-is-glvalue) return as-lvalue(*outer());else return _parent__->inner_.emplace-deref(outer());
constexpr auto& _get-inner_();
Effects: Equivalent to:if constexpr (ref-is-glvalue) return as-lvalue(*outer());else return *_parent__->inner_;
constexpr void _satisfy_();
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%5Frange "25.4.5 Other range refinements [range.refinements]")<_Base_>;constexpr explicit _iterator_(_Parent_& parent) requires (<_Base_>);
Effects: Initializes parent_ with addressof(parent).
For the first overload, also initializes_outer_it__ with std::move(outer).
Then, equivalent to:if (outer() != ranges::end(_parent__->base_)) { inner_it_.template emplace<1>(ranges::begin(update-inner()));satisfy();}
Effects: Initializes outer_it_ withstd::move(i.outer_it_) and_parent__ 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_)));else inner_it_.template emplace<1>(std::get<1>(std::move(i.inner_it_)));
[Note 2:
Const can only be truewhen Base models forward_range.
— _end note_]
constexpr decltype(auto) operator*() const;
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++();
Effects: Equivalent to:visit([](auto& it){ ++it; }, inner_it_);satisfy();return *this;
constexpr void operator++(int);
Effects: Equivalent to ++*this.
Effects: Equivalent to:iterator tmp = *this;++*this;return tmp;
constexpr _iterator_& operator--() requires _ref-is-glvalue_ && [bidirectional_range](range.refinements#concept:bidirectional%5Frange "25.4.5 Other range refinements [range.refinements]")<_Base_> && [_bidirectional-common_](#concept:bidirectional-common "25.7.15.2 Class template join_with_view [range.join.with.view]")<_InnerBase_> && [_bidirectional-common_](#concept:bidirectional-common "25.7.15.2 Class template join_with_view [range.join.with.view]")<_PatternBase_>;
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%5Frange "25.4.5 Other range refinements [range.refinements]")<_Base_> && [_bidirectional-common_](#concept:bidirectional-common "25.7.15.2 Class template join_with_view [range.join.with.view]")<_InnerBase_> && [_bidirectional-common_](#concept:bidirectional-common "25.7.15.2 Class template join_with_view [range.join.with.view]")<_PatternBase_>;
Effects: Equivalent to:iterator tmp = *this;--*this;return tmp;
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<V> && input_range<range_reference_t<V>> && view<Pattern> && concatable<range_reference_t<V>, Pattern> template<bool Const> class join_with_view<V, Pattern>::sentinel { using Parent = maybe-const<Const, join_with_view>; using Base = maybe-const<Const, V>; sentinel_t<_Base_> end_ = sentinel_t<_Base_>(); constexpr explicit sentinel(Parent& parent); public: sentinel() = default;constexpr sentinel(sentinel<!Const> s) requires Const && convertible_to<sentinel_t<V>, sentinel_t<_Base_>>;template<bool OtherConst> requires sentinel_for<sentinel_t<_Base_>, iterator_t<_maybe-const_<OtherConst, V>>> friend constexpr bool operator==(const iterator<OtherConst>& x, const sentinel& y);};}
constexpr explicit _sentinel_(_Parent_& parent);
Effects: Initializes end_ with ranges::end(parent.base_).
constexpr _sentinel_(_sentinel_<!Const> s) requires Const && [convertible_to](concept.convertible#concept:convertible%5Fto "18.4.4 Concept convertible_to [concept.convertible]")<sentinel_t<V>, sentinel_t<_Base_>>;
Effects: Initializes end_ with std::move(s.end_).
template<bool OtherConst> requires [sentinel_for](iterator.concept.sentinel#concept:sentinel%5Ffor "24.3.4.7 Concept 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);
Effects: Equivalent to: return x.outer() == y.end_;