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.

### 3500. `join_view::iterator::operator->()` is bogus

Section: 24.7.12.3 [range.join.iterator] Status: WP Submitter: Michael Schellenberger Costa Opened: 2020-11-15 Last modified: 2021-02-26

Priority: 0

View all other issues in [range.join.iterator].

View all issues with WP status.

Discussion:

There seems to be a copy & paste error in the `join_view::iterator::operator->()` specification. According to 24.7.12.3 [range.join.iterator] p8 it is specified as:

>     constexpr iterator_t<Base> operator->() const
>       requires has-arrow<iterator_t<Base>> && copyable<iterator_t<Base>>;

> > -8- Effects: Equivalent to `return inner_;`

Now `inner_` is of type `iterator_t<range_reference_t<Base>>`. So it is unclear how that should be converted to `iterator_t<Base>`, or why the constraints concern the outer iterator type `iterator_t<Base>`. On the other hand returning `outer_` would not make any sense here.

As far as I can tell we should replace `iterator_t<Base>` with `iterator_t<range_reference_t<Base>>` so that the new specification would read

>     constexpr iterator_t<range_reference_t<Base>> operator->() const
>      requires has-arrow<iterator_t<range_reference_t<Base>>>
>        && copyable<iterator_t<range_reference_t<Base>>>;

> > -8- Effects: Equivalent to `return inner_;`

Generally it would help readability of the specification a lot if we would introduce some exposition only aliases:

>     using OuterIter = iterator_t<Base>; //exposition-only
>     using InnerIter = iterator_t<range_reference_t<Base>> //exposition-only

and use them throughout `join_view::iterator`.

[2020-11-21; Reflector prioritization]

Set priority to 0 and status to Tentatively Ready after six votes in favour during reflector discussions.

[2021-02-26 Approved at February 2021 virtual plenary. Status changed: Tentatively Ready → WP.]

Proposed resolution:

This wording is relative to N4868.

1. Modify 24.7.12.3 [range.join.iterator], class template `join_view::iterator` synopsis, as indicated:

   > >     template<input_range V>
   > >       requires view<V> && input_range<range_reference_t<V>> &&
   > >                (is_reference_v<range_reference_t<V>> ||
   > >                 view<range_value_t<V>>)
   > >     struct join_view<V>::iterator {
   > >     private:
   > >       using Parent = // exposition only
   > >         conditional_t<Const, const join_view, join_view>;
   > >       using Base = conditional_t<Const, const V, V>; // exposition only
   > >       using OuterIter = iterator_t<Base>; //exposition-only
   > >       using InnerIter = iterator_t<range_reference_t<Base>> //exposition-only
   > >       static constexpr bool ref-is-glvalue = // exposition only
   > >         is_reference_v<range_reference_t<Base>>;
   > >       OuterIteriterator_t<Base> outer_ = OuterIteriterator_t<Base>(); // exposition only
   > >       InnerIteriterator_t<range_reference_t<Base>> inner_ = // exposition only
   > >         InnerIteriterator_t<range_reference_t<Base>>();
   > >       Parent* parent_ = nullptr; // exposition only
   > >       constexpr void satisfy(); // exposition only
   > >     public:
   > >       […]
   > >       iterator() = default;
   > >       constexpr iterator(Parent& parent, OuterIteriterator_t<Base> outer);
   > >       constexpr iterator(iterator<!Const> i)
   > >         requires Const &&
   > >                  convertible_to<iterator_t<V>, OuterIteriterator_t<Base>> &&
   > >                  convertible_to<iterator_t<InnerRng>,
   > >                                 InnerIteriterator_t<range_reference_t<Base>>>;

   > >       constexpr decltype(auto) operator*() const { return *inner_; }

   > >       constexpr InnerIteriterator_t<Base> operator->() const
   > >         requires has-arrow<InnerIteriterator_t<Base>>
   > >           && copyable<InnerIteriterator_t<Base>>;

   > >       constexpr iterator& operator++();
   > >       […]

   > […]

   >     constexpr void satisfy(); // exposition only

   > > -5- Effects: Equivalent to:

   > > >     […]
   > > >     if constexpr (ref-is-glvalue)
   > > >       inner_ = InnerIteriterator_t<range_reference_t<Base>>();

   >     constexpr iterator(Parent& parent, OuterIteriterator_t<Base> outer);

   > > […]

   >     constexpr iterator(iterator<!Const> i)
   >       requires Const &&
   >                convertible_to<iterator_t<V>, OuterIteriterator_t<Base>> &&
   >                convertible_to<iterator_t<InnerRng>,
   >                               InnerIteriterator_t<range_reference_t<Base>>>;

   > > […]

   >     constexpr InnerIteriterator_t<Base> operator->() const
   >       requires has-arrow<InnerIteriterator_t<Base>>
   >         && copyable<InnerIteriterator_t<Base>>;

   > > -8- Effects: Equivalent to `return inner_;`