This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of Ready status.

### 3820. `cartesian_product_view::iterator::prev` is not quite right

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

Priority: Not Prioritized

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

View all issues with Ready status.

Discussion:

Currently, `cartesian_product_view::iterator::prev` has the following Effects:

>     auto& it = std::get<N>(current_);
>     if (it == ranges::begin(std::get<N>(parent_->bases_))) {
>       it = cartesian-common-arg-end(std::get<N>(parent_->bases_));
>       if constexpr (N > 0) {
>         prev<N - 1>();
>       }
>     }
>     --it;

which decrements the underlying iterator one by one using recursion. However, when `N == 0`, it still detects if the first iterator has reached the beginning and assigns it to the end, which is not only unnecessary, but also causes `cartesian-common-arg-end` to be applied to the first range, making it ill-formed in some cases, for example:

>     #include <ranges>

>     int main() {
>       auto r = std::views::cartesian_product(std::views::iota(0));
>       r.begin() += 3; // hard error
>     }

This is because, for the first range, `cartesian_product_view::iterator::operator+=` only requires it to model `random_access_range`. However, when `x` is negative, this function will call `prev` and indirectly calls `cartesian-common-arg-end`, since the latter constrains its argument to satisfy `cartesian-product-common-arg`, that is, `common_range<R> || (sized_range<R> && random_access_range<R>)`, which is not the case for the unbounded `iota_view`, resulting in a hard error in `prev`'s function body.

The proposed resolution changes the position of the `if constexpr` so that we just decrement the first iterator and nothing else.

[Kona 2022-11-08; Move to Ready]

Proposed resolution:

This wording is relative to N4917.

1. Modify 26.7.31.3 [ranges.cartesian.iterator] as indicated:

   >     template<size_t N = sizeof...(Vs)>
   >       constexpr void prev();

   > > -6- Effects: Equivalent to:

   > >     auto& it = std::get<N>(current_);
   > >     if constexpr (N > 0) {
   > >       if (it == ranges::begin(std::get<N>(parent_->bases_))) {
   > >         it = cartesian-common-arg-end(std::get<N>(parent_->bases_));
   > >         if constexpr (N > 0) {
   > >           prev<N - 1>();
   > >         }
   > >       }
   > >     }
   > >     --it;