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

### 3517. `join_view::iterator`'s `iter_swap` is underconstrained

Section: 26.7.14.3 [range.join.iterator] Status: WP Submitter: Casey Carter Opened: 2021-01-28 Last modified: 2021-06-07

Priority: Not Prioritized

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

View all issues with WP status.

Discussion:

`std::ranges::join_view::iterator`'s hidden friend `iter_swap` is specified in 26.7.14.3 [range.join.iterator]/16 as:

>     friend constexpr void iter_swap(const iterator& x, const iterator& y)
>       noexcept(noexcept(ranges::iter_swap(x.inner_, y.inner_)));

> > -16- Effects: Equivalent to: `return ranges::iter_swap(x.inner_, y.inner_);`

Notably, the expression `ranges::iter_swap(meow, woof)` is not valid for all iterators `meow` and `woof`, or even all input iterators of the same type as is the case here. This `iter_swap` overload should be constrained to require the type of `iterator::inner_` (`iterator_t<range_reference_t<maybe-const<Const, V>>>)` to satisfy `indirectly_swappable`. Notably this is already the case for `iter_swap` friends of every other iterator adaptor in the Standard Library (`reverse_iterator`, `move_iterator`, `common_iterator`, `counted_iterator`, `filter_view::iterator`, `transform_view::iterator`, and `split_view::inner-iterator`). The omission for `join_view::iterator` seems to have simply been an oversight.

[2021-03-12; Reflector poll]

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

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

Proposed resolution:

This wording is relative to N4878.

1. Modify 26.7.14.3 [range.join.iterator] as indicated:

   > [Drafting note: If 3500 is accepted before this issue, it is kindly suggested to the Project Editor to apply the equivalent replacement of "`iterator_t<range_reference_t<Base>>`" by "`InnerIter`" to the newly inserted `requires`.

   >     namespace std::ranges {
   >       template<input_range V>
   >         requires view<V> && input_range<range_reference_t<V>> &&
   >                  (is_reference_v<range_reference_t<V>> ||
   >                  view<range_value_t<V>>)
   >       template<bool Const>
   >       struct join_view<V>::iterator {
   >         […]

   >         friend constexpr void iter_swap(const iterator& x, const iterator& y)
   >           noexcept(noexcept(ranges::iter_swap(x.inner_, y.inner_)))
   >           requires indirectly_swappable<iterator_t<range_reference_t<Base>>>;
   >       };
   >     }

   […]

   >     friend constexpr void iter_swap(const iterator& x, const iterator& y)
   >       noexcept(noexcept(ranges::iter_swap(x.inner_, y.inner_)))
   >       requires indirectly_swappable<iterator_t<range_reference_t<Base>>>;

   > > -16- Effects: Equivalent to: `return ranges::iter_swap(x.inner_, y.inner_);`