3313. join_view::iterator::operator-- is incorrectly constrained

Section: 25.7.14.3 [range.join.iterator] Status: C++20 Submitter: United States Opened: 2019-11-04 Last modified: 2021-02-25

Priority: 0

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

View all issues with C++20 status.

Discussion:

Addresses US 294

join_view::iterator::operator-- is improperly constrained. In the Effects: clause in paragraph 14, we see the statement:

inner_ = ranges::end(*--outer_);

However, this only well-formed when end returns an iterator, not a sentinel. This requirement is not reflected in the constraints of the function(s).

Eric Niebler:

From the WD, join_view::iterator::operator-- is specified as:

constexpr iterator& operator--()
  requires ref_is_glvalue && bidirectional_range<Base> &&
    bidirectional_range<range_reference_t<Base>>;
-14- Effects: Equivalent to:
if (outer_ == ranges::end(parent_->base_))
  inner_ = ranges::end(*--outer_);
while (inner_ == ranges::begin(*outer_))
  inner_ = ranges::end(*--outer_);
--inner_;
return *this;

The trouble is from the lines that do:

  inner_ = ranges::end(*--outer_);

Clearly this will only compile when *--outer returns a common_range, but nowhere is that requirement stated.

[2019-11 Status to Ready during Tuesday morning issue processing in Belfast.]

Proposed resolution:

This wording is relative to N4835.

  1. Modify 25.7.14.3 [range.join.iterator], class template join_view::iterator synopsis, as indicated:

    constexpr iterator& operator--()
      requires ref_is_glvalue && bidirectional_range<Base> &&
               bidirectional_range<range_reference_t<Base>> &&
               common_range<range_reference_t<Base>>;
    
    constexpr iterator operator--(int)
      requires ref_is_glvalue && bidirectional_range<Base> &&
               bidirectional_range<range_reference_t<Base>> &&
               common_range<range_reference_t<Base>>;
    
  2. Modify 25.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>>;
    

    -14- Effects: Equivalent to:

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

    constexpr iterator operator--(int)
      requires ref_is_glvalue && bidirectional_range<Base> &&
               bidirectional_range<range_reference_t<Base>> &&
               common_range<range_reference_t<Base>>;
    

    -15- Effects: Equivalent to:

    auto tmp = *this;
    --*this;
    return tmp;