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.

### 3700. The `const begin` of the `join_view` family does not require `InnerRng` to be a range

Section: 26.7.14.2 [range.join.view], 26.7.15.2 [range.join.with.view] Status: New Submitter: Hewill Kang Opened: 2022-05-17 Last modified: 2022-06-21

Priority: 3

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

View all issues with New status.

Discussion:

This is a follow-up of LWG 3599.

Currently, the `const` version of `join_view::begin` has the following constraints

>     constexpr auto begin() const
>       requires input_range<const V> &&
>                is_reference_v<range_reference_t<const V>>
>     { return iterator<true>{*this, ranges::begin(base_)}; }

which only requires `InnerRng` to be a reference type, but in some cases, `InnerRng` may not be a `range`. Consider

>     #include <ranges>

>     int main() {
>       auto r = std::views::iota(0, 5)
>              | std::views::split(1);
>       auto s = std::views::single(r);
>       auto j = s | std::views::join;
>       auto f = j.front();
>     }

The reference type of `single_view` is `const split_view&`, which only has the non-`const` version of `begin`, which will cause `view_interface`'s `const front` to be incorrectly instantiated, making `r.front()` unnecessarily ill-formed.

We should add this check for `join_view`'s `const begin`, as well as `join_with_view`.

[2022-06-21; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

This wording is relative to N4910.

1. Modify 26.7.14.2 [range.join.view], class template `join_view` synopsis, as indicated:

   >     namespace std::ranges {

   >       […]

   >       template<input_range V>
   >         requires view<V> && input_range<range_reference_t<V>>
   >       class join_view : public view_interface<join_view<V>> {
   >       private:
   >         […]
   >       public:
   >         […]

   >         constexpr auto begin() {
   >           […]
   >         }

   >         constexpr auto begin() const
   >           requires input_range<const V> &&
   >                    input_range<range_reference_t<const V>> &&
   >                    is_reference_v<range_reference_t<const V>>
   >         { return iterator<true>{*this, ranges::begin(base_)}; }

   >         constexpr auto end() {
   >           […]
   >         }

   >         constexpr auto end() const
   >           requires input_range<const V> &&
   >                    input_range<range_reference_t<const V>> &&
   >                    is_reference_v<range_reference_t<const V>> {
   >           if constexpr (forward_range<const V> &&
   >                         forward_range<range_reference_t<const V>> &&
   >                         common_range<const V> &&
   >                         common_range<range_reference_t<const V>>)
   >             return iterator<true>{*this, ranges::end(base_)};
   >           else
   >             return sentinel<true>{*this};
   >         }
   >       };

   >       […]

   >     }

2. Modify 26.7.15.2 [range.join.with.view], class template `join_with_view` synopsis, as indicated:

   >     namespace std::ranges {

   >       […]

   >       template<input_range V, forward_range Pattern>
   >       requires view<V> && input_range<range_reference_t<V>>
   >             && view<Pattern>
   >             && compatible-joinable-ranges<range_reference_t<V>, Pattern>
   >       class join_with_view : public view_interface<join_with_view<V, Pattern>> {
   >       private:
   >         […]
   >       public:
   >         […]

   >         constexpr auto begin() {
   >           […]
   >         }
   >         constexpr auto begin() const
   >           requires input_range<const V> &&
   >                    forward_range<const Pattern> &&
   >                    input_range<range_reference_t<const V>> &&
   >                    is_reference_v<range_reference_t<const V>> {
   >           return iterator<true>{*this, ranges::begin(base_)};
   >         }

   >         constexpr auto end() {
   >           […]
   >         }
   >         constexpr auto end() const
   >           requires input_range<const V> && forward_range<const Pattern> &&
   >                    input_range<range_reference_t<const V>> &&
   >                    is_reference_v<range_reference_t<const V>> {
   >           using InnerConstRng = range_reference_t<const V>;
   >           if constexpr (forward_range<const V> && forward_range<InnerConstRng> &&
   >                         common_range<const V> && common_range<InnerConstRng>)
   >             return iterator<true>{*this, ranges::end(base_)};
   >           else
   >             return sentinel<true>{*this};
   >         }
   >       };

   >       […]

   >     }