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.

4303. std::decay_t in the specification of ranges::distance is problematic

Section: 24.4.4.3 [range.iter.op.distance] Status: New Submitter: Jiang An Opened: 2025-07-24 Last modified: 2025-07-27

Priority: Not Prioritized

View all other issues in [range.iter.op.distance].

View all issues with New status.

Discussion:

This is discovered when implementing the resolution LWG 4242(i). Per LWG 4242(i), it is intended to allow ranges::distance to handle volatile-qualified iterator values. However, the uses of decay_t (established per LWG 3664(i)) are still problematic, because when sized_sentinel_for<S, decay_t<I>> is modeled, there's no semantic or syntactic requirement that S shall work with volatile-qualified I.

If we implement the constraint as is, there will still be some underconstrained cases. E.g. When the operator== or operator- intendedly rejects volatile-qualified iterators. And even when they accept volatile-qualified iterators, the additional semantic requirements imposed by sized_sentinel_for<S, decay_t<I>> are still undesired.

I think we should only decay arrays and keep volatile for non-array arguments.

Proposed resolution:

This wording is relative to this CD preview draft.

  1. Modify 24.2 [iterator.synopsis], header <iterator> synopsis, as indicated:

    […]
    namespace std: {
      […]
      // 24.4.4.3 [range.iter.op.distance], ranges::distance
     
      template<class T>
        using distance-iterator-t =               // exposition only
          conditional_t<is_array_v<remove_reference_t<T>>,
            decay_t<T>, remove_const_t<remove_reference_t<T>>>;
      
      template<class I, sentinel_for<I> S>
        requires (!sized_sentinel_for<S, I>)
        constexpr iter_difference_t<I> distance(I first, S last); // freestanding
      template<class I, sized_sentinel_for<decay_tdistance-iterator-t<I>> S>
        constexpr iter_difference_t<decay_tdistance-iterator-t<I>> distance(I&& first, S last); // freestanding
      template<range R>
        constexpr range_difference_t<R> distance(R&& r); // freestanding
      […]
    }
    
  2. Modify 24.4.4.3 [range.iter.op.distance] as indicated:

    template<class I, sized_sentinel_for<decay_tdistance-iterator-t<I>> S>
      constexpr iter_difference_t<decay_tdistance-iterator-t<I>> distance(I&& first, S last);
    

    -3- Effects: Equivalent to:

    if constexpr (!is_array_v<remove_reference_t<I>>)
      return last - first;
    else
      return last - static_cast<decay_t<I>>(first);