Issue 3483: transform_view::iterator's difference is overconstrained (original) (raw)


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

3483. transform_view::_iterator_'s difference is overconstrained

Section: 25.7.9.3 [range.transform.iterator], 25.7.23.3 [range.elements.iterator] Status: C++23 Submitter: Casey Carter Opened: 2020-09-04 Last modified: 2023-11-22

Priority: 0

View all other issues in [range.transform.iterator].

View all issues with C++23 status.

Discussion:

The difference operation for transform_view::_iterator_ is specified in 25.7.9.3 [range.transform.iterator] as:

friend constexpr difference_type operator-(const iterator& x, const iterator& y) requires random_access_range<_Base_>;

-22- Effects: Equivalent to: return x._current_ - y._current_;

The member _current_ is an iterator of type iterator_t<_Base_>, where _Base_ is V for transform_view<V, F>::_iterator_<false>and const V for transform_view<V, F>::_iterator_<true>. The difference of iterators that appears in the above Effects: element is notably well-defined if their type models sized_sentinel_for<iterator_t<_Base_>, iterator_t<_Base_>> which random_access_range<_Base_> refines. This overstrong requirement seems to be simply the result of an oversight; it has been present since P0789R0, without — to my recollection — ever having been discussed. We should relax this requirement to provide difference capability for transform_view's iterators whenever the underlying iterators do.

[2020-09-08; Reflector discussion]

During reflector discussions it was observed that elements_view::_iterator_ has the same issue and the proposed wording has been extended to cover this template as well.

[2020-09-13; Reflector prioritization]

Set priority to 0 and status to Tentatively Ready after seven votes in favour during reflector discussions.

[2020-11-09 Approved In November virtual meeting. Status changed: Tentatively Ready → WP.]

Proposed resolution:

This wording is relative to N4861.

  1. Modify 25.7.9.3 [range.transform.iterator] as indicated:

    namespace std::ranges {
    template<input_range V, copy_constructible F>
    requires view && is_object_v &&
    regular_invocable<F&, range_reference_t> &&

        _can-reference_<invoke_result_t<F&, range_reference_t<V>>>  

template
class transform_view<V, F>::iterator {
public:
[…]
friend constexpr iterator operator-(iterator i, difference_type n)
requires random_access_range<_Base_>;
friend constexpr difference_type operator-(const iterator& x, const iterator& y)
requires random_access_range<_Base_>sized_sentinel_for<iterator_t<_Base_>, iterator_t<_Base_>>;
[…]
};
}

[…]

friend constexpr difference_type operator-(const iterator& x, const iterator& y)
requires random_access_range<_Base_>sized_sentinel_for<iterator_t<_Base_>, iterator_t<_Base_>>;

-22- Effects: return x._current_ - y._current_; 2. Modify 25.7.23.3 [range.elements.iterator] as indicated:
namespace std::ranges {
template<input_range V, size_t N>
requires view && has-tuple-element<range_value_t, N> &&
has-tuple-element<remove_reference_t<range_reference_t>, N>
template
class elements_view<V, N>::iterator { // exposition only
[…]
friend constexpr iterator operator-(iterator x, difference_type y)
requires random_access_range<_Base_>;
friend constexpr difference_type operator-(const iterator& x, const iterator& y)
requires random_access_range<_Base_>sized_sentinel_for<iterator_t<_Base_>, iterator_t<_Base_>>;
};
}

[…]

constexpr difference_type operator-(const iterator& x, const iterator& y)
requires random_access_range<_Base_>sized_sentinel_for<iterator_t<_Base_>, iterator_t<_Base_>>;

-21- Effects: return x._current_ - y._current_;