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-sum
be the sum ofscaled-distance(N)
for every integer0 ≤ N ≤ sizeof...(Vs)
.-8- Preconditions:
scaled-sum
can 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-tuple
be 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);