Title
lazy_split_view<input_view>::inner-iterator::base() && invalidates outer iterators
Status
c++23
Section
[range.lazy.split.inner]
Submitter
Tim Song

Created on 2021-09-13.00:00:00 last changed 4 months ago

Messages

Date: 2021-10-14.09:56:08

Proposed resolution:

This wording is relative to N4892.

  1. Modify [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);

Date: 2021-10-14.00:00:00

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

Date: 2021-09-15.00:00:00

[ 2021-09-24; Reflector poll ]

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

Date: 2021-09-13.00:00:00

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.

History
Date User Action Args
2023-11-22 15:47:43adminsetstatus: wp -> c++23
2021-10-14 09:56:08adminsetmessages: + msg12142
2021-10-14 09:56:08adminsetstatus: voting -> wp
2021-09-29 12:57:28adminsetstatus: ready -> voting
2021-09-24 17:55:02adminsetmessages: + msg12072
2021-09-24 17:55:02adminsetstatus: new -> ready
2021-09-17 15:34:14adminsetmessages: + msg12036
2021-09-13 00:00:00admincreate