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.
cartesian_product_view::iterator::operator- should pass by referenceSection: 25.7.33.3 [range.cartesian.iterator] Status: C++23 Submitter: Hewill Kang Opened: 2022-08-27 Last modified: 2023-11-22
Priority: Not Prioritized
View all other issues in [range.cartesian.iterator].
View all issues with C++23 status.
Discussion:
There are two problems with cartesian_product_view::iterator::operator-.
distance-from is not const-qualified, which would cause
the common version of operator- to produce a hard error inside the function body
since it invokes distance-from on a const iterator object.
Second, the non-common version of operator- passes iterator by value,
which unnecessarily prohibits subtracting default_sentinel from an lvalue
iterator whose first underlying iterator is non-copyable.
Even if we std::move it into operator-, the other overload will still be ill-formed
since it returns -(i - s) which will calls i's deleted copy constructor.
The proposed resolution is to add const qualification to distance-from
and make the non-common version of iterator::operator- pass by const reference.
Although the Tuple parameter of distance-from is guaranteed to be
copyable, I think it would be more appropriate to pass it by reference.
[2022-09-08]
As suggested by Tim Song, the originally submitted proposed wording has been refined to use
const Tuple& instead of Tuple&.
[2022-09-23; Reflector poll]
Set status to Tentatively Ready after six votes in favour during reflector poll.
[2022-11-12 Approved at November 2022 meeting in Kona. Status changed: Voting → WP.]
Proposed resolution:
This wording is relative to N4917.
Modify [ranges.cartesian.iterator] as indicated:
namespace std::ranges { template<input_range First, forward_range... Vs> requires (view<First> && ... && view<Vs>) template<bool Const> class cartesian_product_view<First, Vs...>::iterator { public: […] friend constexpr difference_type operator-(const iterator& i, default_sentinel_t) requires cartesian-is-sized-sentinel<Const, sentinel_t, First, Vs...>; friend constexpr difference_type operator-(default_sentinel_t, const iterator& i) requires cartesian-is-sized-sentinel<Const, sentinel_t, First, Vs...>; […] private: […] template<class Tuple> constexpr difference_type distance-from(const Tuple& t) const; // exposition only […] }; }[…]
template<class Tuple> constexpr difference_type distance-from(const Tuple& t) const;-7- Let:
(7.1) —
scaled-size(N)be the product ofstatic_cast<difference_type>(ranges::size(std::get<N>(parent_->bases_)))andscaled-size(N + 1)ifN < sizeof...(Vs), otherwisestatic_cast<difference_type>(1);(7.2) —
scaled-distance(N)be the product ofstatic_cast<difference_type>(std::get<N>(current_) - std::get<N>(t))andscaled-size(N + 1); and(7.3) —
scaled-sumbe the sum ofscaled-distance(N)for every integer0 ≤ N ≤ sizeof...(Vs).-8- Preconditions:
scaled-sumcan be represented bydifference_type.-9- Returns:
scaled-sum.[…]
friend constexpr difference_type operator-(const iterator& i, default_sentinel_t) requires cartesian-is-sized-sentinel<Const, sentinel_t, First, Vs...>;-32- Let
end-tuplebe an object of a type that is a specialization oftuple, such that:
(32.1) —
std::get<0>(end-tuple)has the same value asranges::end(std::get<0>(i.parent_->bases_));(32.2) —
std::get<N>(end-tuple)has the same value asranges::begin(std::get<N>(i.parent_->bases_))for every integer1 ≤ N ≤ sizeof...(Vs).-33- Effects: Equivalent to:
return i.distance-from(end-tuple);friend constexpr difference_type operator-(default_sentinel_t, const iterator& i) requires cartesian-is-sized-sentinel<Const, sentinel_t, First, Vs...>;-34- Effects: Equivalent to:
return -(i - s);