3763. Should range adaptor iterators only provide iterator_category when its difference_type is not an integer-class type?

Section: [range.repeat.iterator], [range.cartesian.iterator] Status: New Submitter: Hewill Kang Opened: 2022-08-27 Last modified: 2023-02-07 02:58:54 UTC

Priority: 3

View all issues with New status.


After P2259, the range adaptor' iterators only provide iterator_category member when the underlying range models forward_range, which is mainly based on the premise that all valid C++20 forward iterators meet the C++17 input iterator requirements.

However, this is not strictly correct. When the underlying range's difference_type is an integer-class type, its iterator does not conform Cpp17InputIterator.

Although iterator_traits<I>::iterator_category will still deduce the correct category in this case since these iterators have no reference member, it might be misleading to provide these incorrect member types.

Do we need to aggressively prohibit these iterators from providing iterator_category when their difference type is an integer-class type?

The proposed resolution makes repeat_view::iterator conditionally provide iterator_category, because it explicitly mentions IOTA-DIFF-T(index-type) in the definition of difference_type, which makes it consistent with LWG 3670.

It also removes the reference member type of cartesian_product_view::iterator, which prevents iterator_traits<I>::iterator_category from being aliased to its member iterator_category, so that iterator_traits<I>::iterator_category will not always be an input_iterator_tag when its difference_type is an integer-class type.

This is also consistent with other range adaptors, as none of them have a reference member type.

[2022-09-23; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

This wording is relative to N4917.

  1. Modify [range.repeat.iterator] as indicated:

    namespace std::ranges {
      template<move_constructible W, semiregular Bound = unreachable_sentinel_t>
        requires (is_object_v<W> && same_as<W, remove_cv_t<W>> &&
                  (is-integer-like<Bound> || same_as<Bound, unreachable_sentinel_t>))
      class repeat_view<W, Bound>::iterator {
        using index-type =                  // exposition only
          conditional_t<same_as<Bound, unreachable_sentinel_t>, ptrdiff_t, Bound>;
        const W* value_ = nullptr;          // exposition only
        index-type current_ = index-type(); // exposition only
        constexpr explicit iterator(const W* value, index-type b = index-type());   // exposition only
        using iterator_concept = random_access_iterator_tag;
        using iterator_category = random_access_iterator_tag;           // present only if difference_type
                                                                        // is an integral type
        using value_type = W;
        using difference_type = conditional_t<is-signed-integer-like<index-type>,
  2. Modify [range.cartesian.iterator] as indicated:

    namespace std::ranges {
      template<input_range First, forward_range... Vs>
        requires (view<First> && ... && view<Vs>)
      template<bool Const>
      class cartesian_product_view<First, Vs...>::iterator {
        using iterator_category = input_iterator_tag;
        using iterator_concept  = see below;
        using value_type = tuple<range_value_t<maybe-const<Const, First>>,
          range_value_t<maybe-const<Const, Vs>>...>;
        using reference = tuple<range_reference_t<maybe-const<Const, First>>,
          range_reference_t<maybe-const<Const, Vs>>...>;
        using difference_type = see below;
        constexpr autoreference operator[](difference_type n) const
          requires cartesian-product-is-random-access<Const, First, Vs...>;


    constexpr autoreference operator[](difference_type n) const
      requires cartesian-product-is-random-access<Const, First, Vs...>;

    -24- Effects: return *((*this) + n);