Title
join_view::iterator::operator->() is bogus
Status
c++23
Section
[range.join.iterator]
Submitter
Michael Schellenberger Costa

Created on 2020-11-15.00:00:00 last changed 3 months ago

Messages

Date: 2021-02-26.17:31:29

Proposed resolution:

This wording is relative to N4868.

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

Date: 2021-02-26.00:00:00

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

Date: 2020-11-15.00:00:00

[ 2020-11-21; Reflector prioritization ]

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

Date: 2020-11-15.00:00:00

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

History
Date User Action Args
2023-11-22 15:47:43adminsetstatus: wp -> c++23
2021-02-26 17:31:29adminsetmessages: + msg11705
2021-02-26 17:31:29adminsetstatus: ready -> wp
2020-11-21 13:58:36adminsetmessages: + msg11621
2020-11-21 13:58:36adminsetstatus: new -> ready
2020-11-15 17:20:13adminsetmessages: + msg11614
2020-11-15 00:00:00admincreate