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.

3520. iter_move and iter_swap are inconsistent for transform_view::iterator

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

Priority: 2

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

View all issues with C++23 status.

Discussion:

For transform_view::iterator, iter_move is specified to operate on the transformed value but iter_swap is specified to operate on the underlying iterator.

Consider the following test case:

struct X { int x; int y; };
std::vector<X> v = {...};
auto t = v | views::transform(&X::x);
ranges::sort(t);

iter_swap on t's iterators would swap the whole X, including the y part, but iter_move will only move the x data member and leave the y part intact. Meanwhile, ranges::sort can use both iter_move and iter_swap, and does so in at least one implementation. The mixed behavior means that we get neither "sort Xs by their x data member" (as ranges::sort(v, {}, &X::x) would do), nor "sort the x data member of these Xs and leave the rest unchanged", as one might expect, but instead some arbitrary permutation of y. This seems like a questionable state of affairs.

[2021-03-12; Reflector poll]

Set priority to 2 following reflector poll.

[2021-03-12; LWG telecon]

Set status to Tentatively Ready after discussion and poll.

FAN
900

[2021-06-07 Approved at June 2021 virtual plenary. Status changed: Voting → WP.]

Proposed resolution:

This wording is relative to N4878.

  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 {
        […]
        friend constexpr void iter_swap(const iterator& x, const iterator& y)
          noexcept(noexcept(ranges::iter_swap(x.current_, y.current_)))
          requires indirectly_swappable<iterator_t<Base>>;
      };
    }
    
    […]
    friend constexpr void iter_swap(const iterator& x, const iterator& y)
      noexcept(noexcept(ranges::iter_swap(x.current_, y.current_)))
      requires indirectly_swappable<iterator_t<Base>>;
    

    -23- Effects: Equivalent to ranges::iter_swap(x.current_, y.current_).