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

Section: 25.7.14.3 [range.join.iterator] Status: C++23 Submitter: Michael Schellenberger Costa Opened: 2020-11-15 Last modified: 2023-11-22

Priority: 0

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

View all issues with C++23 status.

Discussion:

There seems to be a copy & paste error in the join_view::iterator::operator->() specification. According to 25.7.14.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 25.7.14.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_;