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

**Section:** 26.7.32.3 [range.cartesian.iterator] **Status:** WP
**Submitter:** Hewill Kang **Opened:** 2022-11-08 **Last modified:** 2023-02-13 10:17:57 UTC

**Priority: **Not Prioritized

**View all other** issues in [range.cartesian.iterator].

**View all issues with** WP 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]*

*[2023-02-13 Status changed: Voting → WP.]*

**Proposed resolution:**

This wording is relative to N4917.

Modify [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;