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

### 3791. `join_view::iterator::operator--` may be ill-formed

Section: 26.7.14.3 [range.join.iterator] Status: New Submitter: Hewill Kang Opened: 2022-09-30 Last modified: 2022-10-12

Priority: 3

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

View all issues with New status.

Discussion:

Currently, `join_view::iterator::operator--` has the following Effects:

>     if (outer_ == ranges::end(parent_->base_))
>       inner_ = ranges::end(*--outer_);
>     while (inner_ == ranges::begin(*outer_))
>       inner_ = ranges::end(*--outer_);
>     --inner_;
>     return *this;

which uses `ranges::end(*--outer_)` to get the sentinel of the inner range. However, `*--outer_` may return an rvalue reference to a non-borrowed range, in which case calling `ranges::end` will be ill-formed, for example:

>     #include <ranges>
>     #include <vector>

>     int main() {
>       std::vector<std::vector<int>> v;
>       auto r = v | std::views::transform([](auto& x) -> auto&& { return std::move(x); })
>                  | std::views::join;
>       auto e = --r.end(); // hard error
>     }

The proposed resolution uses a temporary reference to bind `*--outer_`, so that `ranges::end` is always invoked on an lvalue range, which is consistent with the behavior of `join_with_view::iterator::operator--`.

[2022-10-12; Reflector poll]

Set priority to 3 after reflector poll.

"Could introduce an `as_lvalue` lambda (like `auto as_lvalue = []<class T>(T&& x) -> T& { return (T&)x; };`) and use it throughout."

Proposed resolution:

This wording is relative to N4917.

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

   >     constexpr iterator& operator--()
   >       requires ref-is-glvalue && bidirectional_range<Base> &&
   >                bidirectional_range<range_reference_t<Base>> &&
   >                common_range<range_reference_t<Base>>;

   -13- Effects: Equivalent to:

   >     if (outer_ == ranges::end(parent_->base_)) {
   >       auto&& inner = *--outer_;
   >       inner_ = ranges::end(inner*--outer_);
   >     }
   >     while (trueinner_ == ranges::begin(*outer_)) {
   >       if (auto&& tmp = *outer_; inner_ == ranges::begin(tmp)) {
   >         auto&& inner = *--outer_;
   >         inner_ = ranges::end(inner*--outer_);
   >       } else {
   >         break;
   >       }
   >     }
   >     --inner_;
   >     return *this;