4092. The monotonic version of common_iterator::operator== is underconstrained

Section: 24.5.5.1 [common.iterator], 24.5.5.6 [common.iter.cmp] Status: New Submitter: Hewill Kang Opened: 2024-05-01 Last modified: 2024-07-21

Priority: 3

View other active issues in [common.iterator].

View all other issues in [common.iterator].

View all issues with New status.

Discussion:

common_iterator has the following equality operator:

template<class I2, sentinel_for<I> S2>
    requires sentinel_for<S, I2>
  friend constexpr bool operator==(
    const common_iterator& x, const common_iterator<I2, S2>& y);

which is quite useful when wrapping a C++20 input_iterator that does not model equality_comparable so that the quality operator required by the Cpp17InputIterator can still be synthesized.

However, the function signature does not check the correlation between I2 and I, which allows a common_iterator wrapping two completely unrelated iterators to validly compare (demo):

common_iterator<string::iterator, unreachable_sentinel_t> i1;
common_iterator<list<int>::iterator, unreachable_sentinel_t> i2;
i1 == i2; // unfortunately compile

The proposed resolution requires common_with<I, I2> to be satisfied to enhance semantics, which is also consistent with the signature of counted_iterator::operator==.

[2024-06-24; Reflector poll]

Set priority to 3 after reflector poll. Seems unlikely to be a problem in practice. Proposed resolution would make the two operator== overloads ambiguous.

Previous resolution [SUPERSEDED]:

This wording is relative to N4981.

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

    namespace std {
      template<input_or_output_iterator I, sentinel_for<I> S>
        requires (!same_as<I, S> && copyable<I>)
      class common_iterator {
      public:
        […]
        template<classcommon_with<I> I2, sentinel_for<I> S2>
          requires sentinel_for<S, I2>
        friend constexpr bool operator==(
          const common_iterator& x, const common_iterator<I2, S2>& y);
        […]
      };
      […]
    }
    
  2. Modify 24.5.5.6 [common.iter.cmp] as indicated:

    template<classcommon_with<I> I2, sentinel_for<I> S2>
      requires sentinel_for<S, I2>
    friend constexpr bool operator==(
      const common_iterator& x, const common_iterator<I2, S2>& y);
    

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

    […]

[2024-07-09; Hewill provides improved wording]

Proposed resolution:

This wording is relative to N4986.

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

    namespace std {
      template<input_or_output_iterator I, sentinel_for<I> S>
        requires (!same_as<I, S> && copyable<I>)
      class common_iterator {
      public:
        […]
        template<classcommon_with<I> I2, sentinel_for<I> S2>
          requires sentinel_for<S, I2>
        friend constexpr bool operator==(
          const common_iterator& x, const common_iterator<I2, S2>& y);
        template<classcommon_with<I> I2, sentinel_for<I> S2>
          requires sentinel_for<S, I2> && equality_comparable_with<I, I2>
        friend constexpr bool operator==(
          const common_iterator& x, const common_iterator<I2, S2>& y);
        […]
      };
      […]
    }
    
  2. Modify 24.5.5.6 [common.iter.cmp] as indicated:

    template<classcommon_with<I> I2, sentinel_for<I> S2>
      requires sentinel_for<S, I2>
    friend constexpr bool operator==(
      const common_iterator& x, const common_iterator<I2, S2>& y);
    

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

    […]

    template<classcommon_with<I> I2, sentinel_for<I> S2>
      requires sentinel_for<S, I2> && equality_comparable_with<I, I2>
    friend constexpr bool operator==(
      const common_iterator& x, const common_iterator<I2, S2>& y);
    

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

    […]