3748. common_iterator and counted_iterator' operator- are missing cast to return type

Section: 24.5.5.6 [common.iter.cmp], 24.5.7.5 [counted.iter.nav] Status: New Submitter: Hewill Kang Opened: 2022-08-01 Last modified: 2022-08-23

Priority: 3

View all issues with New status.

Discussion:

Both common_iterator and counted_iterator explicitly specify that the return type of their operator- is iter_difference_t<I2>, however, given that the calculated type may be iter_difference_t<I>, we should do an explicit conversion here since the latter is not necessarily implicitly convertible to the former:

#include <ranges>

struct Y;

struct X {
  X(Y);
  using difference_type =
#ifdef __GNUC__
  std::ranges::__detail::__max_diff_type;
#elif defined(_MSC_VER)
  std::_Signed128;
#endif
  int& operator*() const;
  X& operator++();
  void operator++(int);
};

struct Y {
  using difference_type = std::ptrdiff_t;
  int& operator*() const;
  Y& operator++();
  void operator++(int);
};

int main() {
  std::counted_iterator<Y> y;
  return std::counted_iterator<X>(y) - y; // hard error in stdlibc++ and MSVC-STL
}

Daniel:

This issue shouldn't we voted until a decision for LWG 3749 has been made, because the first part of it overlaps with LWG 3749's second part.

[2022-08-23; Reflector poll]

Set priority to 3 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, so this issue should only affect counted_iterator."

"If the difference types of I and I2 are different then the operator- can't be used to model sized_sentinel_for, since i - i2 and i2 - i would have different types. Providing operator- under such circumstances seems to be of dubious value."

Proposed resolution:

This wording is relative to N4910.

  1. Modify 24.5.5.6 [common.iter.cmp] as indicated:

    template<sized_sentinel_for<I> I2, sized_sentinel_for<I> S2>
      requires sized_sentinel_for<S, I2>
    friend constexpr 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<iter_difference_t<I2>>(get<i>(x.v_) - get<j>(y.v_)), where i is x.v_.index() and j is y.v_.index().

  2. Modify 24.5.7.5 [counted.iter.nav] as indicated:

    template<common_with<I> I2>
      friend constexpr iter_difference_t<I2> operator-(
        const counted_iterator& x, const counted_iterator<I2>& y);
    

    -13- Preconditions: x and y refer to elements of the same sequence (24.5.7.1 [counted.iterator]).

    -14- Effects: Equivalent to: return static_cast<iter_difference_t<I2>>(y.length - x.length);