4170. contiguous_iterator should require to_address(I{})

Section: 24.3.4.14 [iterator.concept.contiguous] Status: New Submitter: Casey Carter Opened: 2024-11-01 Last modified: 2024-11-01

Priority: Not Prioritized

View all other issues in [iterator.concept.contiguous].

View all issues with New status.

Discussion:

The design intent of the contiguous_iterator concept is that iterators can be converted to pointers denoting the same sequence of elements. This enables a common range [i, j) or counted range i + [0, n) to be processed with extremely efficient low-level C or assembly code that operates on [to_address(i), to_address(j)) (respectively to_address(i) + [0, n)).

A value-initialized iterator I{} can be used to denote the empty ranges [I{}, I{}) and I{} + [0, 0). While the existing semantic requirements of contiguous_iterator enable us to convert both dereferenceable and past-the-end iterators with to_address, converting ranges involving value-initialized iterators to pointer ranges additionally needs to_address(I{}) to be well-defined. Note that to_address is already implicitly equality-preserving for contiguous_iterator arguments. Given this additional requirement to_address(I{}) == to_address(I{}) and to_address(I{}) == to_address(I{)) + 0 both hold, so the two types of empty ranges involving value-initialized iterators convert to empty pointer ranges as desired.

Proposed resolution:

This wording is relative to N4993.

  1. Modify 24.3.4.14 [iterator.concept.contiguous] as indicated:

    -1- The contiguous_iterator concept provides a guarantee that the denoted elements are stored contiguously in memory.

    template<class I>
      concept contiguous_iterator =
        random_access_iterator<I> &&
        derived_from<ITER_CONCEPT(I), contiguous_iterator_tag> &&
        is_lvalue_reference_v<iter_reference_t<I>> &&
        same_as<iter_value_t<I>, remove_cvref_t<iter_reference_t<I>>> &&
        requires(const I& i) {
          { to_address(i) } -> same_as<add_pointer_t<iter_reference_t<I>>>;
        };
    

    -2- Let a and b be dereferenceable iterators and c be a non-dereferenceable iterator of type I such that b is reachable from a and c is reachable from b, and let D be iter_difference_t<I>. The type I models contiguous_iterator only if

    1. (2.1) — to_address(a) == addressof(*a),

    2. (2.2) — to_address(b) == to_address(a) + D(b - a),

    3. (2.3) — to_address(c) == to_address(a) + D(c - a),

    4. (2.?) — to_address(I{}) is well-defined,

    5. (2.4) — ranges::iter_move(a) has the same type, value category, and effects as std::move(*a), and

    6. (2.5) — if ranges::iter_swap(a, b) is well-formed, it has effects equivalent to ranges::swap(*a, *b).