Title
`std::decay_t` in the specification of `ranges::distance` is problematic
Status
new
Section
[range.iter.op.distance]
Submitter
Jiang An

Created on 2025-07-24.00:00:00 last changed yesterday

Messages

Date: 2025-08-29.17:49:45

Proposed resolution:

This wording is relative to N5014.

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

    […]
    namespace std: {
      […]
      // [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 [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);
    
Date: 2025-08-15.00:00:00

[ 2025-08-29; Reflector poll ]

Set priority to 4 after reflector poll.

Date: 2025-07-27.09:01:01

This is discovered when implementing the resolution LWG 4242. Per LWG 4242, it is intended to allow `ranges::distance` to handle `volatile`-qualified iterator values. However, the uses of `decay_t` (established per LWG 3664) 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.

History
Date User Action Args
2025-08-29 17:49:45adminsetmessages: + msg14980
2025-07-27 09:01:01adminsetmessages: + msg14914
2025-07-24 00:00:00admincreate