3564. transform_view::iterator<true>::value_type and iterator_category should use const F&

Section: 25.7.9.3 [range.transform.iterator] Status: C++23 Submitter: Tim Song Opened: 2021-06-06 Last modified: 2023-11-22

Priority: 2

View all other issues in [range.transform.iterator].

View all issues with C++23 status.

Discussion:

Iterators obtained from a const transform_view invoke the transformation function as const, but the value_type and iterator_category determination uses plain F&, i.e., non-const.

[2021-06-14; Reflector poll]

Set priority to 2 after reflector poll, send to SG9 for design clarification. Should r and as_const(r) guarantee same elements?

[2022-07-08; Reflector poll]

SG9 has decided to proceed with this PR in its 2021-09-13 telecon and not block the issue for a paper on the more general const/non-const problem.

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

[2022-07-15; LWG telecon: move to Ready]

[2022-07-25 Approved at July 2022 virtual plenary. Status changed: Ready → WP.]

Proposed resolution:

This wording is relative to N4885.

  1. Modify 25.7.9.3 [range.transform.iterator] as indicated:

    namespace std::ranges {
      template<input_range V, copy_constructible F>
        requires view<V> && is_object_v<F> &&
                 regular_invocable<F&, range_reference_t<V>> &&
                 can-reference<invoke_result_t<F&, range_reference_t<V>>>
      template<bool Const>
      class transform_view<V, F>::iterator {
      private:
        […]
      public:
        using iterator_concept = see below;
        using iterator_category = see below; // not always present
        using value_type =
          remove_cvref_t<invoke_result_t<maybe-const<Const, F>&, 
          range_reference_t<Base>>>;
        using difference_type = range_difference_t<Base>;  
        […]
      };
    }
    

    -1- […]

    -2- The member typedef-name iterator_category is defined if and only if Base models forward_range. In that case, iterator::iterator_category is defined as follows: Let C denote the type iterator_traits<iterator_t<Base>>::iterator_category.

    1. (2.1) — If is_lvalue_reference_v<invoke_result_t<maybe-const<Const, F>&, range_reference_t<Base>>> is true, then

      1. (2.1.1) — […]

      2. (2.1.2) — […]

    2. (2.2) — Otherwise, iterator_category denotes input_iterator_tag.