This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of WP status.
Section: 26.7.28.5 [range.chunk.inner.iter] Status: WP Submitter: Hewill Kang Opened: 2023-01-06 Last modified: 2023-02-13
Priority: Not Prioritized
View all issues with WP status.
Discussion:
For the input version of chunk_view, its inner-iterator::operator*() simply dereferences the underlying input iterator. However, there are no customized iter_move and iter_swap overloads for the inner-iterator, which prevents customized behavior when applying views::chunk to a range whose iterator type is a proxy iterator, for example:
#include <algorithm>
#include <cassert>
#include <ranges>
#include <sstream>
#include <vector>
int main() {
auto ints = std::istringstream{"0 1 2 3 4"};
std::vector<std::string> vs{"the", "quick", "brown", "fox"};
auto r = std::views::zip(vs, std::views::istream<int>(ints))
| std::views::chunk(2)
| std::views::join;
std::vector<std::tuple<std::string, int>> res;
std::ranges::copy(std::move_iterator(r.begin()), std::move_sentinel(r.end()),
std::back_inserter(res));
assert(vs.front().empty()); // assertion failed
}
zip iterator has a customized iter_move behavior, but since there is no iter_move specialization for inner-iterator, when we try to move elements in chunk_view, move_iterator will fallback to use the default implementation of iter_move, making strings not moved as expected from the original vector but copied instead.
[2023-02-01; Reflector poll]
Set status to Tentatively Ready after five votes in favour during reflector poll.
[2023-02-13 Status changed: Voting → WP.]
Proposed resolution:
This wording is relative to N4917.
Modify 26.7.28.5 [range.chunk.inner.iter] as indicated:
[…]namespace std::ranges { template<view V> requires input_range<V> class chunk_view<V>::inner-iterator { chunk_view* parent_; // exposition only constexpr explicit inner-iterator(chunk_view& parent) noexcept; // exposition only public: […] friend constexpr difference_type operator-(default_sentinel_t y, const inner-iterator& x) requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>; friend constexpr difference_type operator-(const inner-iterator& x, default_sentinel_t y) requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>; friend constexpr range_rvalue_reference_t<V> iter_move(const inner-iterator& i) noexcept(noexcept(ranges::iter_move(*i.parent_->current_))); friend constexpr void iter_swap(const inner-iterator& x, const inner-iterator& y) noexcept(noexcept(ranges::iter_swap(*x.parent_->current_, *y.parent_->current_))) requires indirectly_swappable<iterator_t<V>>; }; }friend constexpr range_rvalue_reference_t<V> iter_move(const inner-iterator& i) noexcept(noexcept(ranges::iter_move(*i.parent_->current_)));-?- Effects: Equivalent to: return ranges::iter_move(*i.parent_->current_);
friend constexpr void iter_swap(const inner-iterator& x, const inner-iterator& y) noexcept(noexcept(ranges::iter_swap(*x.parent_->current_, *y.parent_->current_))) requires indirectly_swappable<iterator_t<V>>;-?- Effects: Equivalent to: ranges::iter_swap(*x.parent_->current_, *y.parent_->current_).