Title
join_view::iterator::operator-- may be ill-formed
Status
resolved
Section
[range.join.iterator]
Submitter
Hewill Kang

Created on 2022-09-30.00:00:00 last changed 12 months ago

Messages

Date: 2023-03-22.00:00:00

[ 2023-03-22 Resolved by the adoption of P2770R0 in Issaquah. Status changed: New → Resolved. ]

Date: 2022-10-15.00:00:00

[ 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."

This wording is relative to N4917.

  1. Modify [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;
    
Date: 2022-09-30.00:00:00

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--.

History
Date User Action Args
2023-03-23 11:42:08adminsetstatus: new -> resolved
2022-10-12 14:38:10adminsetmessages: + msg12860
2022-10-01 17:55:26adminsetmessages: + msg12839
2022-09-30 00:00:00admincreate