[range.join] (original) (raw)
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]
join_view flattens a view of ranges into a view.
Given a subexpression E, the expressionviews::join(E) is expression-equivalent tojoin_view<views::all_t<decltype((E))>>{E}.
[Example 1: vector<string> ss{"hello", " ", "world", "!"};for (char ch : ss | views::join) cout << ch; — _end example_]
25.7.14.2 Class template join_view [range.join.view]
namespace std::ranges { template<input_range V> requires view<V> && input_range<range_reference_t<V>> class join_view : public view_interface<join_view<V>> { private: using InnerRng = range_reference_t<V>; template<bool Const> struct iterator; template<bool Const> struct sentinel; V base_ = V(); non-propagating-cache<iterator_t<V>> outer_; non-propagating-cache<remove_cv_t<_InnerRng_>> inner_; public: join_view() requires default_initializable<V> = default;constexpr explicit join_view(V base);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_>;return iterator<use_const>{*this, ranges::begin(base_)};} else { outer_ = ranges::begin(base_);return iterator<false>{*this};} } constexpr auto begin() const requires forward_range<const V> && is_reference_v<range_reference_t<const V>> && input_range<range_reference_t<const V>> { 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>>{*this, ranges::end(base_)};else return sentinel<simple-view<V>>{*this};} constexpr auto end() const requires forward_range<const V> && is_reference_v<range_reference_t<const V>> && input_range<range_reference_t<const V>> { if constexpr (forward_range<range_reference_t<const V>> && common_range<const V> && common_range<range_reference_t<const V>>) return iterator<true>{*this, ranges::end(base_)};else return sentinel<true>{*this};} };template<class R> explicit join_view(R&&) -> join_view<views::all_t<R>>;}
constexpr explicit join_view(V base);
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<V> && input_range<range_reference_t<V>> template<bool Const> struct join_view<V>::iterator { private: using Parent = maybe-const<Const, join_view>; using Base = maybe-const<Const, V>; using OuterIter = iterator_t<_Base_>; using InnerIter = iterator_t<range_reference_t<_Base_>>; static constexpr bool ref-is-glvalue = is_reference_v<range_reference_t<_Base_>>;OuterIter outer_ = OuterIter(); optional<_InnerIter_> inner_; Parent* parent_ = nullptr; constexpr void satisfy(); constexpr OuterIter& outer(); constexpr const OuterIter& outer() const; constexpr iterator(Parent& parent, OuterIter outer) requires forward_range<_Base_>; constexpr explicit iterator(Parent& parent) requires (<_Base_>); public: using iterator_concept = see below;using iterator_category = see below; using value_type = range_value_t<range_reference_t<_Base_>>;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_>;constexpr decltype(auto) operator*() const { return **inner_; } constexpr InnerIter operator->() const requires 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_>;};}
_iterator_::iterator_concept is defined as follows:
- Otherwise, if ref-is-glvalue is true and_Base_ and range_reference_t<_Base_> each model forward_range, then iterator_concept denotesforward_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,Base models forward_range, andrange_reference_t<_Base_> models forward_range.
In that case,_iterator_::iterator_category is defined as follows:
- Let OUTERC denoteiterator_traits<iterator_t<_Base_>>::iterator_category, and let INNERC denoteiterator_traits<iterator_t<range_reference_t<_Base_>>>::iterator_category.
- If_OUTERC_ and INNERC each modelderived_from<bidirectional_iterator_tag> andrange_reference_t<_Base_> models common_range,iterator_category denotes bidirectional_iterator_tag.
- Otherwise, if_OUTERC_ and INNERC each modelderived_from<forward_iterator_tag>, iterator_category denotes forward_iterator_tag.
- Otherwise,iterator_category denotes input_iterator_tag.
_iterator_::difference_type denotes the type:common_type_t< range_difference_t<_Base_>, range_difference_t<range_reference_t<_Base_>>>
join_view iterators use the satisfy function to skip over empty inner ranges.
constexpr _OuterIter_& _outer_();constexpr const _OuterIter_& _outer_() const;
Returns: outer_ if Base models forward_range; otherwise, *_parent__->outer_.
constexpr void _satisfy_();
Effects: Equivalent to:auto update_inner = [this](const iterator_t<_Base_>& x) -> auto&& { if constexpr (ref-is-glvalue) return *x;else return _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%5Frange "25.4.5 Other range refinements [range.refinements]")<_Base_>;
Effects: Initializes outer_ with std::move(outer) and_parent__ with addressof(parent); then calls satisfy().
constexpr explicit _iterator_(_Parent_& parent) requires (<_Base_>);
Effects: Initializes parent_ with addressof(parent); then calls satisfy().
constexpr _iterator_(_iterator_<!Const> i) requires Const && [convertible_to](concept.convertible#concept:convertible%5Fto "18.4.4 Concept convertible_to [concept.convertible]")<iterator_t<V>, _OuterIter_> && [convertible_to](concept.convertible#concept:convertible%5Fto "18.4.4 Concept convertible_to [concept.convertible]")<iterator_t<_InnerRng_>, _InnerIter_>;
Effects: Initializes outer_ with std::move(i.outer_),inner_ with std::move(i.inner_), and_parent__ with i.parent_.
[Note 1:
Const can only be truewhen Base models forward_range.
— _end note_]
constexpr _InnerIter_ operator->() const requires [_has-arrow_](range.utility.helpers#concept:has-arrow "25.5.2 Helper concepts [range.utility.helpers]")<_InnerIter_> && [copyable](concepts.object#concept:copyable "18.6 Object concepts [concepts.object]")<_InnerIter_>;
Effects: Equivalent to: return *inner_;
constexpr _iterator_& operator++();
Let inner-range be:
Effects: Equivalent to:if (++*inner_ == ranges::end(as-lvalue(inner-range))) { ++outer();satisfy();} return *this;
constexpr void operator++(int);
Effects: Equivalent to: ++*this.
Effects: Equivalent to:auto tmp = *this;++*this;return tmp;
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;
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%5Frange "25.4.5 Other range refinements [range.refinements]")<_Base_> && [equality_comparable](concept.equalitycomparable#concept:equality%5Fcomparable "18.5.4 Concept equality_comparable [concept.equalitycomparable]")<iterator_t<range_reference_t<_Base_>>>;
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%5Fswappable "24.3.7.4 Concept indirectly_swappable [alg.req.ind.swap]")<_InnerIter_>;
Effects: Equivalent to: return 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<V> && input_range<range_reference_t<V>> template<bool Const> struct join_view<V>::sentinel { private: using Parent = maybe-const<Const, join_view>; using Base = maybe-const<Const, V>; sentinel_t<_Base_> end_ = sentinel_t<_Base_>(); public: sentinel() = default;constexpr explicit sentinel(Parent& parent);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_;