3388. view iterator types have ill-formed <=> operators

Section: 25.6.4.3 [range.iota.iterator], 25.7.9.3 [range.transform.iterator], 25.7.23.3 [range.elements.iterator] Status: C++20 Submitter: Jonathan Wakely Opened: 2020-02-07 Last modified: 2021-02-25

Priority: 0

View other active issues in [range.iota.iterator].

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

View all issues with C++20 status.

Discussion:

25.6.4.3 [range.iota.iterator] and 25.7.9.3 [range.transform.iterator] and 25.7.23.3 [range.elements.iterator] all declare operator<=> similar to this:

friend constexpr compare_three_way_result_t<W> operator<=>(
    const iterator& x, const iterator& y)
  requires totally_ordered<W> && three_way_comparable<W>;

Similar to issue 3347 and issue 3387, this is ill-formed if three_way_comparable<W> is not satisfied, because compare_three_way_result_t<W> is invalid. This declaration is instantiated when the enclosing iterator type is instantiated, making any use of iota_view<W, B>::iterator ill-formed when three_way_comparable<W> is not satisfied.

We can either add an exposition-only safe-compare-three-way-result-t alias that denotes void or std::nonesuch for spaceship-less types, so the declaration is valid (and then disabled by the constraints), or simply make them return auto.

[2020-02 Prioritized as IMMEDIATE Monday morning in Prague]

Proposed resolution:

This wording is relative to N4849.

  1. Modify 25.6.4.3 [range.iota.iterator] as indicated:

    namespace std::ranges {
      template<class W, class Bound>
      struct iota_view<W, Bound>::iterator {
        […]
        friend constexpr compare_three_way_result_t<W>auto operator<=>(
            const iterator& x, const iterator& y)
          requires totally_ordered<W> && three_way_comparable<W>;
        […]
      };
      […]
    }
    
    […]
    friend constexpr compare_three_way_result_t<W>auto
      operator<=>(const iterator& x, const iterator& y)
        requires totally_ordered<W> && three_way_comparable<W>;
    

    -19- Effects: Equivalent to: return x.value_ <=> y.value_;

  2. Modify 25.7.9.3 [range.transform.iterator] as indicated:

    namespace std::ranges {
      template<class V, class F>
      template<bool Const>
      class transform_view<V, F>::iterator {
        […]
        friend constexpr compare_three_way_result_t<iterator_t<Base>>auto 
          operator<=>(const iterator& x, const iterator& y)
            requires random_access_range<Base> && three_way_comparable<iterator_t<Base>>;
        […]
      };
      […]
    }
    
    […]
    friend constexpr compare_three_way_result_t<iterator_t<Base>>auto
      operator<=>(const iterator& x, const iterator& y)
        requires random_access_range<Base> && three_way_comparable<iterator_t<Base>>;
    

    -19- Effects: Equivalent to: return x.current_ <=> y.current_;

  3. Modify 25.7.23.3 [range.elements.iterator] as indicated:

    namespace std::ranges {
      template<class V, size_t N>
      template<bool Const>
      class elements_view<V, N>::iterator {
        […]
        friend constexpr compare_three_way_result_t<iterator_t<base-t>>auto 
          operator<=>(const iterator& x, const iterator& y)
            requires random_access_range<base-t> && three_way_comparable<iterator_t<base-t>>;
        […]
      };
      […]
    }
    
    […]
    friend constexpr compare_three_way_result_t<iterator_t<base-t>>auto
      operator<=>(const iterator& x, const iterator& y)
        requires random_access_range<base-t> && three_way_comparable<iterator_t<base-t>>;
    

    -18- Effects: Equivalent to: return x.current_ <=> y.current_;