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.

### 3761. `cartesian_product_view::iterator::operator-` should pass by reference

Section: 26.7.31.3 [ranges.cartesian.iterator] Status: WP Submitter: Hewill Kang Opened: 2022-08-27 Last modified: 2022-11-17

Priority: Not Prioritized

View all other issues in [ranges.cartesian.iterator].

View all issues with WP status.

Discussion:

There are two problems with `cartesian_product_view::iterator::operator-`.

First, `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.

1. Modify 26.7.31.3 [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:

   > > 1. (7.1) — `scaled-size(N)` be the product of `static_cast<difference_type>(ranges::size(std::get<N>(parent_->bases_)))` and `scaled-size(N + 1)` if `N < sizeof...(Vs)`, otherwise `static_cast<difference_type>(1)`;

   > > 2. (7.2) — `scaled-distance(N)` be the product of `static_cast<difference_type>(std::get<N>(current_) - std::get<N>(t))` and `scaled-size(N + 1)`; and

   > > 3. (7.3) — `scaled-sum` be the sum of `scaled-distance(N)` for every integer `0 ≤ N ≤ sizeof...(Vs)`.

   > > -8- Preconditions: `scaled-sum` can be represented by `difference_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 of `tuple`, such that:

   > > 1. (32.1) — `std::get<0>(end-tuple)` has the same value as `ranges::end(std::get<0>(i.parent_->bases_))`;

   > > 2. (32.2) — `std::get<N>(end-tuple)` has the same value as `ranges::begin(std::get<N>(i.parent_->bases_))` for every integer `1 ≤ 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);`