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

4558. drop_view and drop_while_view should be const-iterable for input-only range

Section: 25.7.12.2 [range.drop.view], 25.7.13.2 [range.drop.while.view] Status: LEWG Submitter: Hewill Kang Opened: 2026-03-25 Last modified: 2026-06-16

Priority: Not Prioritized

View other active issues in [range.drop.view].

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

View all issues with LEWG status.

Discussion:

After P3725R3, filter_view can be const-iterable as long as the underlying range is input-only range, implying that LEWG do believe that providing const-iterable properties for views does have some value.

Its turn out that drop_view and drop_while_view are also const-iterable in the same situation, because they do not internally need to cache the begin call on the first iteration.

[2026-05-29 LWG reflector poll; status: New → LEWG]

Previous resolution [SUPERSEDED]

This wording is relative to N5032.

  1. Modify 25.7.12.2 [range.drop.view] as indicated:

    namespace std::ranges {
      template<view V>
      class drop_view : public view_interface<drop_view<V>> {
      public:
        […]
        constexpr auto begin()
          requires (!(simple-view<V> &&
                      ((random_access_range<const V> && sized_range<const V>) ||
                       (input_range<const V> && !forward_range<const V>))));
        constexpr auto begin() const
          requires (random_access_range<const V> && sized_range<const V>) ||
                   (input_range<const V> && !forward_range<const V>);
        […]
      };
      […]
    }
    
    constexpr auto begin()
       requires (!(simple-view<V> &&
                   ((random_access_range<const V> && sized_range<const V>) ||
                    (input_range<const V> && !forward_range<const V>))));
    constexpr auto begin() const
      requires (random_access_range<const V> && sized_range<const V>) ||
               (input_range<const V> && !forward_range<const V>);
    

    -3- Returns: ranges::next(ranges::begin(base_), count_, ranges::end(base_)).

    -4- Remarks: In order to provide the amortized constant-time complexity required by the range concept when drop_view models forward_range, the first overload caches the result within the drop_view for use on subsequent calls.

  2. Modify 25.7.13.2 [range.drop.while.view] as indicated:

    namespace std::ranges {
      template<view V, class Pred>
        requires input_range<V> && is_object_v<Pred> &&
                 indirect_unary_predicate<const Pred, iterator_t<V>>
      class drop_while_view : public view_interface<drop_while_view<V, Pred>> {
      public:
        […]
    
        constexpr auto begin()
          requires (!(simple-view<V> &&
                      input_range<const V> && !forward_range<const V>));
        constexpr auto begin() const
          requires (input_range<const V> && !forward_range<const V> &&
                    indirect_unary_predicate<const Pred, iterator_t<const V>>);
    
        constexpr auto end() requires (!simple-view<V>) { return ranges::end(base_); }
        constexpr auto end() const requires range<const V> { return ranges::end(base_); }
    
        […]
      };
      […]
    }
    
    […]
    constexpr auto begin()
      requires (!(simple-view<V> &&
                  input_range<const V> && !forward_range<const V>));
    constexpr auto begin() const
      requires (input_range<const V> && !forward_range<const V> &&
                indirect_unary_predicate<const Pred, iterator_t<const V>>);
    
    

    -3-Preconditions: pred_.has_value() is true.

    -4-Returns: ranges::find_if_not(base_, cref(*pred_)).

    -5-Remarks: In order to provide the amortized constant-time complexity required by the range concept when drop_while_view models forward_range, the first overloadcall caches the result within the drop_while_view for use on subsequent calls.

[2026-06-16; Hewill provides new wording]

Proposed resolution:

This wording is relative to N5032.

  1. Modify 25.2 [ranges.syn], header <ranges> synopsis, as indicated:

    // mostly freestanding
    #include <compare>              // see [compare.syn]
    #include <initializer_list>     // see [initializer.list.syn]
    #include <iterator>             // see [iterator.synopsis]
    
    namespace std::ranges {
      […]
      template<class T>
        concept sized-random-access-range = see below;              // exposition only
    
      template<class T>
        concept single-pass-range = see below;                      // exposition only
      […]
    }
    
  2. Modify25.4.6 [range.refinements] as indicated:

    -8- The exposition-only concept sized-random-access-range specifies the requirements of a range type that is sized and allows random access to its elements.

    template<class T>
      concept sized-random-access-range =           // exposition only
        random_access_range<T> && sized_range<T>;
    

    [Note 1: This concept constrains some parallel algorithm overloads; see [algorithms]. — end note]

    -?- The exposition-only concept single-pass-range specifies the requirements of a range type that can only be traversed once and does not guarantee multi-pass safety.

    [Drafting note: The input_range is not required in single-pass-range because views like drop_view can also apply on output_range.]
    template<class T>
      concept single-pass-range =                   // exposition only
        range<T> && !forward_range<T>;
    
  3. Modify 25.7.8.2 [range.filter.view] as indicated:

    [Drafting note: This just reuse single-pass-range concept in `filter_view to reduce.]
    namespace std::ranges {
      template<input_range V, indirect_unary_predicate<iterator_t<V>> Pred>
        requires view<V> && is_object_v<Pred>
      class filter_view : public view_interface<filter_view<V, Pred>> {
        […]
        constexpr iterator<true> begin() const
          requires single-pass-range(input_range<const V> && !forward_range<const V> &&
                   indirect_unary_predicate<const Pred, iterator_t<const V>>);
        […]
        constexpr sentinel<true> end() const
          requires single-pass-range(input_range<const V> && !forward_range<const V> &&
                   indirect_unary_predicate<const Pred, iterator_t<const V>>) {
          return sentinel<true>{*this};
        }
        […]
      };
      […]
    }
    […]
    constexpr iterator<true> begin() const
       requires single-pass-range(input_range<const V> && !forward_range<const V> &&
                indirect_unary_predicate<const Pred, iterator_t<const V>>);
    

    -6-Preconditions: pred_.has_value() is true.

  4. Modify 25.7.12.2 [range.drop.view] as indicated:

    [Drafting note: This reuse exposition only sized-random-access-range, which is defined in the same place as single-pass-range, to reduce typing.]
    namespace std::ranges {
      template<view V>
      class drop_view : public view_interface<drop_view<V>> {
      public:
        […]
        constexpr auto begin()
          requires (!(simple-view<V> &&
                      (sized-random-access-rangerandom_access_range<const V> && sized_range<const V> ||
                       single-pass-range<const V>)));
        constexpr auto begin() const
          requires sized-random-access-rangerandom_access_range<const V> && sized_range<const V> ||
                   single-pass-range<const V>;
        […]
      };
      […]
    }
    
    […]
    constexpr auto begin()
       requires (!(simple-view<V> &&
                   (sized-random-access-rangerandom_access_range<const V> && sized_range<const V> ||
                    single-pass-range<const V>)));
    constexpr auto begin() const
      requires sized-random-access-rangerandom_access_range<const V> && sized_range<const V> ||
               single-pass-range<const V>;
    

    -3-Returns: ranges::next(ranges::begin(base_), count_, ranges::end(base_)).

    -4-Remarks: In order to provide the amortized constant-time complexity required by the range concept when drop_view models forward_range, the first overload caches the result within the drop_view for use on subsequent calls.

  5. Modify 25.7.13.2 [range.drop.while.view] as indicated:

    namespace std::ranges {
      template<view V, class Pred>
        requires input_range<V> && is_object_v<Pred> &&
                 indirect_unary_predicate<const Pred, iterator_t<V>>
      class drop_while_view : public view_interface<drop_while_view<V, Pred>> {
      public:
        […]
    
        constexpr auto begin()
          requires (!(simple-view<V> && single-pass-range<const V>));
        constexpr auto begin() const
          requires single-pass-range<const V> &&
                   indirect_unary_predicate<const Pred, iterator_t<const V>>;
    
        constexpr auto end() requires (!simple-view<V>) { return ranges::end(base_); }
        constexpr auto end() const requires range<const V> { return ranges::end(base_); }
    
        […]
      };
      […]
    }
    
    […]
    constexpr auto begin()
      requires (!(simple-view<V> && single-pass-range<const V>));
    constexpr auto begin() const
      requires single-pass-range<const V> &&
               indirect_unary_predicate<const Pred, iterator_t<const V>>;
    

    -3-Preconditions: pred_.has_value() is true.

    -4-Returns: ranges::find_if_not(base_, cref(*pred_)).

    -5-Remarks: In order to provide the amortized constant-time complexity required by the range concept when drop_while_view models forward_range, the first overload call caches the result within the drop_while_view for use on subsequent calls.