This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of WP status.

### 3591. `lazy_split_view<input_view>::inner-iterator::base() &&` invalidates outer iterators

Section: 26.7.16.5 [range.lazy.split.inner] Status: WP Submitter: Tim Song Opened: 2021-09-13 Last modified: 2021-10-14

Priority: Not Prioritized

View all issues with WP status.

Discussion:

The `base() &&` members of iterator adaptors (and iterators of range adaptors) invalidate the adaptor itself by moving from the contained iterator. This is generally unobjectionable — since you are calling `base()` on an rvalue, it is expected that you won't be using it afterwards.

But `lazy_split_view<input_view>::inner-iterator::base() &&` is special: the iterator being moved from is stored in the `lazy_split_view` itself and shared between the inner and outer iterators, so the operation invalidates not just the `inner-iterator` on which it is called, but also the `outer-iterator` from which the `inner-iterator` was obtained. This spooky-action-at-a-distance behavior can be surprising, and the value category of the inner iterator seems to be too subtle to base it upon.

The PR below constrains this overload to forward ranges. Forward iterators are copyable anyway, but moving could potentially be more efficient.

[2021-09-24; Reflector poll]

Set status to Tentatively Ready after six votes in favour during reflector poll.

[2021-10-14 Approved at October 2021 virtual plenary. Status changed: Voting → WP.]

Proposed resolution:

This wording is relative to N4892.

1. Modify 26.7.16.5 [range.lazy.split.inner] as indicated:

   > [Drafting note: The constraint uses `forward_range<V>` since that's the condition for caching in `lazy_split_view`.]

   > >     namespace std::ranges {
   > >       template<input_range V, forward_range Pattern>
   > >         requires view<V> && view<Pattern> &&
   > >                  indirectly_comparable<iterator_t<V>, iterator_t<Pattern>, ranges::equal_to> &&
   > >                  (forward_range<V> || tiny-range<Pattern>)
   > >       template<bool Const>
   > >       struct lazy_split_view<V, Pattern>::inner-iterator {
   > >       private:
   > >         […]
   > >       public:
   > >         […]

   > >         constexpr const iterator_t<Base>& base() const &;
   > >         constexpr iterator_t<Base> base() && requires forward_range<V>;

   > >         […]
   > >       };

   > […]

   >     constexpr iterator_t<Base> base() && requires forward_range<V>;

   > > -4- Effects: Equivalent to: `return std::move(i_.current);`