Title
counted_iterator is missing preconditions
Status
c++23
Section
[iterators.counted]
Submitter
Michael Schellenberger Costa

Created on 2020-07-29.00:00:00 last changed 5 months ago

Messages

Date: 2020-11-09.21:40:50

Proposed resolution:

This wording is relative to N4861.

  1. Modify [counted.iter.elem] as indicated:

    constexpr decltype(auto) operator*();
    constexpr decltype(auto) operator*() const
      requires dereferenceable<const I>;
    

    -?- Preconditions: length > 0.

    -1- Effects: Equivalent to: return *current;

  2. Modify [counted.iter.cust] as indicated:

    friend constexpr iter_rvalue_reference_t<I>
      iter_move(const counted_iterator& i)
        noexcept(noexcept(ranges::iter_move(i.current)))
        requires input_iterator<I>;
    

    -?- Preconditions: i.length > 0.

    -1- Effects: Equivalent to: return ranges::iter_move(i.current);

    template<indirectly_swappable<I> I2>
      friend constexpr void
        iter_swap(const counted_iterator& x, const counted_iterator<I2>& y)
          noexcept(noexcept(ranges::iter_swap(x.current, y.current)));
    

    -?- Preconditions: x.length > 0 and y.length > 0.

    -1- Effects: Equivalent to: return ranges::iter_swap(x.current, y.current);

Date: 2020-11-09.00:00:00

[ 2020-11-09 Approved In November virtual meeting. Status changed: Tentatively Ready → WP. ]

Date: 2020-08-21.00:00:00

[ 2020-08-21 Issue processing telecon: moved to Tentatively Ready. ]

Date: 2020-07-29.00:00:00

C++20 introduces a new iterator counted_iterator that keeps track of the end of its range via an additional exposition only member length.

Consequently, there are several preconditions for many member functions of counted_iterator, but it seems some are missing:

  1. operator*

    Here we have no precondition regarding length. However, given that length denotes the distance to the end of the range it should be invalid to dereference a counted_iterator with length 0.

    Moreover, operator[] has a precondition of "n < length". Consider the following code snippet:

    int some_ints[] = {0,1,2};
    counted_iterator<int*> i{some_ints, 0};
    

    Here "i[0]" would be invalid due to the precondition "n < length". However, "*i" would be a valid expression. This violates the definition of operator[] which states according to [expr.sub] p1:

    […] The expression E1[E2] is identical (by definition) to *((E1)+(E2)) […]

    Substituting E2->0 we get

    […] The expression E1[0] is identical (by definition) to *(E1) […]

    With the current wording counted_iterator violates that definition and we should add to operator*:

    Preconditions: length > 0.

  2. iter_move

    This is a similar case. We have only the Effects element:

    Effects: Equivalent to: return ranges::iter_move(i.current);

    However, looking at the requirements of ranges::iter_move we have in [iterator.cust.move] p2:

    If ranges::iter_move(E) is not equal to *E, the program is ill-formed, no diagnostic required.

    This clearly requires that for counted_iterator::iter_move to be well-formed, we need counted_iterator::operator* to be well formed. Consequently we should also add the same precondition to counted_iterator::iter_move:

    Preconditions: length > 0.

  3. iter_swap

    This is essentially the same arguing as for counted_iterator::iter_move. The essential observation is that ranges::iter_swap is defined in terms of ranges::iter_move (see [iterator.cust.swap]) so it must have the same preconditions and we should add:

    Preconditions: length > 0.

History
Date User Action Args
2023-11-22 15:47:43adminsetstatus: wp -> c++23
2020-11-09 21:40:50adminsetmessages: + msg11586
2020-11-09 21:40:50adminsetstatus: ready -> wp
2020-08-21 17:46:27adminsetmessages: + msg11443
2020-08-21 17:46:27adminsetstatus: new -> ready
2020-08-02 18:09:52adminsetmessages: + msg11427
2020-07-29 00:00:00admincreate