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

`counted_iterator`

is missing preconditions**Section:** 25.5.7 [iterators.counted] **Status:** C++23
**Submitter:** Michael Schellenberger Costa **Opened:** 2020-07-29 **Last modified:** 2023-11-22

**Priority: **Not Prioritized

**View all issues with** C++23 status.

**Discussion:**

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:

`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 7.6.1.2 [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`

.`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 25.3.3.1 [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`

.`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 25.3.3.2 [iterator.cust.swap]) so it must have the same preconditions and we should add:*Preconditions:*`length > 0`

.

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

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

**Proposed resolution:**

This wording is relative to N4861.

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

Modify 25.5.7.7 [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);`