Title
cartesian_product_view::iterator::distance-from ignores the size of last underlying range
Status
c++23
Section
[range.cartesian.iterator]
Submitter
Patrick Palka

Created on 2022-10-25.00:00:00 last changed 13 months ago

Messages

Date: 2022-11-17.00:42:33

Proposed resolution:

This wording is relative to N4917.

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

    template<class Tuple>
      constexpr difference_type distance-from(Tuple t);
    

    -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).

Date: 2022-11-12.00:00:00

[ 2022-11-12 Approved at November 2022 meeting in Kona. Status changed: Voting → WP. ]

Date: 2022-11-15.00:00:00

[ 2022-11-04; Reflector poll ]

Set status to Tentatively Ready after seven votes in favour during reflector poll.

Date: 2022-10-25.00:00:00

The helper scaled-size(N) from the wording for P2374R4's cartesian_product_view::iterator::distance-from is recursively specified as:

Let 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);

Intuitively, scaled-size(N) is the size of the cartesian product of all but the first N underlying ranges. Thus scaled-size(sizeof...(Vs)) ought to just yield the size of the last underlying range (since there are 1 + sizeof...(Vs) underlying ranges), but according to this definition it yields 1. Similarly at the other extreme, scaled-size(0) should yield the product of the sizes of all the underlying ranges, but it instead yields that of all but the last underlying range.

For cartesian_product_views of two or more underlying ranges, this causes the relevant operator- overloads to compute wrong distances, e.g.

int x[] = {1, 2, 3};
auto v = views::cartesian_product(x, x);
auto i = v.begin() + 5;  // *i == {2, 3}
assert(*i == tuple{2, 3});
assert(i - v.begin() == 5); // fails, expects 3, because:
// scaled-sum = scaled-distance(0) + scaled-distance(1)
//            = ((1 - 0) * scaled-size(1)) + ((2 - 0) * scaled-size(2))
//            = 1 + 2 instead of 3 + 2

The recursive condition should probably use <= instead of <.

History
Date User Action Args
2023-11-22 15:47:43adminsetstatus: wp -> c++23
2022-11-17 00:42:33adminsetmessages: + msg13086
2022-11-17 00:42:33adminsetstatus: voting -> wp
2022-11-08 03:46:49adminsetstatus: ready -> voting
2022-11-04 20:58:40adminsetmessages: + msg12920
2022-11-04 20:58:40adminsetstatus: new -> ready
2022-10-28 12:47:16adminsetmessages: + msg12892
2022-10-25 00:00:00admincreate