This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of New status.

3749. common_iterator should handle integer-class difference types

Section: 25.5.5 [iterators.common] Status: New Submitter: Hewill Kang Opened: 2022-08-01 Last modified: 2022-08-23

Priority: 2

View all issues with New status.

Discussion:

The partial specialization of iterator_traits for common_iterator is defined in 25.5.5.1 [common.iterator] as

template<input_iterator I, class S>
struct iterator_traits<common_iterator<I, S>> {
  using iterator_concept = see below;
  using iterator_category = see below;
  using value_type = iter_value_t<I>;
  using difference_type = iter_difference_t<I>;
  using pointer = see below;
  using reference = iter_reference_t<I>;
};

where difference_type is defined as iter_difference_t<I> and iterator_category is defined as at least input_iterator_tag. However, when difference_type is an integer-class type, common_iterator does not satisfy Cpp17InputIterator, which makes iterator_category incorrectly defined as input_iterator_tag.

Since the main purpose of common_iterator is to be compatible with the legacy iterator system, which is reflected in its efforts to try to provide the operations required by C++17 iterators even if the underlying iterator does not support it. We should handle this case of difference type incompatibility as well.

The proposed solution is to provide a C++17 conforming difference type by clamping the integer-class type to ptrdiff_t.

Daniel:

The second part of this issue provides an alternative resolution for the first part of LWG 3748 and solves the casting problem mentioned in LWG 3748 as well.

[2022-08-23; Reflector poll]

Set priority to 2 after reflector poll.

"I think common_iterator should reject iterators with integer-class difference types since it can't possibly achieve the design intent of adapting them to Cpp17Iterators."

"I'm not yet convinced that we need to outright reject such uses, but I'm pretty sure that we shouldn't mess with the difference type and that the PR is in the wrong direction."

Proposed resolution:

This wording is relative to N4910.

  1. Modify 25.5.5.1 [common.iterator] as indicated:

    [Drafting note: common_iterator requires iterator type I must model input_or_output_iterator which ensures that iter_difference_t<I> is a signed-integer-like type. The modification of common_iterator::operator- is to ensure that the pair of common_iterator<I, S> models sized_sentinel_for when sized_sentinel_for<I, S> is modeled for iterator type I with an integer-class difference type and its sentinel type S.]

    namespace std {
      template<class D>
        requires is-signed-integer-like<D>
      using make-cpp17-diff-t = conditional_t<signed_integral<D>, D, ptrdiff_t>;  // exposition only
    
      template<input_or_output_iterator I, sentinel_for<I> S>
        requires (!same_as<I, S> && copyable<I>)
      class common_iterator {
      public:
        […]
        template<sized_sentinel_for<I> I2, sized_sentinel_for<I> S2>
          requires sized_sentinel_for<S, I2>
        friend constexpr make-cpp17-diff-t<iter_difference_t<I2>> operator-(
          const common_iterator& x, const common_iterator<I2, S2>& y);
        […]
      };
      
      template<class I, class S>
      struct incrementable_traits<common_iterator<I, S>> {
        using difference_type = make-cpp17-diff-t<iter_difference_t<I>>;
      };
    
      template<input_iterator I, class S>
      struct iterator_traits<common_iterator<I, S>> {
        using iterator_concept = see below;
        using iterator_category = see below;
        using value_type = iter_value_t<I>;
        using difference_type = make-cpp17-diff-t<iter_difference_t<I>>;
        using pointer = see below;
        using reference = iter_reference_t<I>;
      };
    }
    
  2. Modify 25.5.5.6 [common.iter.cmp] as indicated:

    [Drafting note: If this issue is voted in at the same time as LWG 3748, the editor is kindly informed that the changes indicated below supersede those of the before mentioned issues first part.]

    template<sized_sentinel_for<I> I2, sized_sentinel_for<I> S2>
      requires sized_sentinel_for<S, I2>
    friend constexpr make-cpp17-diff-t<iter_difference_t<I2>> operator-(
      const common_iterator& x, const common_iterator<I2, S2>& y);
    

    -5- Preconditions: x.v_.valueless_by_exception() and y.v_.valueless_by_exception() are each false.

    -6- Returns: 0 if i and j are each 1, and otherwise static_cast<make-cpp17-diff-t<iter_difference_t<I2>>>(get<i>(x.v_) - get<j>(y.v_)), where i is x.v_.index() and j is y.v_.index().