enumerate_view
may invoke UB for sized common non-forward underlying rangesSection: 25.7.24 [range.enumerate] Status: WP Submitter: Patrick Palka Opened: 2023-04-07 Last modified: 2024-04-02
Priority: 3
View all issues with WP status.
Discussion:
For a sized common range, enumerate_view::end()
is specified to call
ranges::distance
. But ranges::distance
is not necessarily well-defined
for a sized non-forward range after calling ranges::begin
(according to
25.4.3 [range.sized]).
enumerate_view::begin()
followed by enumerate_view::end()
may invoke UB
and thus make enumerate_view
potentially unusable for such ranges.
I suppose we might need to instead call and cache the result of
ranges::distance
from enumerate_view::begin()
for such ranges.
[2022-04-12; Patrick Palka provides wording]
The proposed wording follows the suggestion provided by Tim Song, to simply make enumerate
non-common for this case.
[2023-05-24; Reflector poll]
Set priority to 3 after reflector poll.
[Kona 2023-11-10; move to Ready]
[Tokyo 2024-03-23; Status changed: Voting → WP.]
Proposed resolution:
This wording is relative to N4944.
Modify 25.7.24.2 [range.enumerate.view], class template class enumerate_view
synopsis, as indicated:
[…] constexpr auto end() requires (!simple-view<V>) { if constexpr (forward_range<V> && common_range<V> && sized_range<V>) return iterator<false>(ranges::end(base_), ranges::distance(base_)); else return sentinel<false>(ranges::end(base_)); } constexpr auto end() const requires range-with-movable-references<const V> { if constexpr (forward_range<const V> && common_range<const V> && sized_range<const V>) return iterator<true>(ranges::end(base_), ranges::distance(base_)); else return sentinel<true>(ranges::end(base_)); } […]