Document number | P3763R0 |
Date | 2025-06-21 |
Audience | LEWG, SG9 (Ranges) |
Reply-to | Hewill Kang <hewillk@gmail.com> |
reserve_hint
members from view classes
This paper removes the reserve_hint
members of view classes when their size
members
are already provided.
Initial revision.
After P2846,
sized_range
always implies
approximately_sized_range
by its definition ([range.sized]):
template<class T> concept sized_range = approximately_sized_range<T> && requires(T& t) { ranges::size(t); };And
size
and reserve_hint
members of the view class are currently with the
following
constraint:
template<range R> struct some_view : public view_interface<some_view<R>> { […] constexpr auto size() requires sized_range<R> constexpr auto size() const requires sized_range<const R> constexpr auto reserve_hint() requires approximately_sized_range<R> constexpr auto reserve_hint() const requires approximately_sized_range<const R> […] };This turns outs that all view classes with a valid
size
member
also have a
reserve_hint
member.
However, those reserve_hint
in such cases is actually redundant because
ranges::reserve_hint
always
prefers the size
member when available ([range.prim.size.hint]):
-2- Given a subexpression
E
with typeT
, let t be an lvalue that denotes the reified object forE
. Then:
(2.1) — If
ranges::size(E)
is a valid expression,ranges::reserve_hint(E)
is expression-equivalent toranges::size(E)
.(2.2) — Otherwise, if
auto(t.reserve_hint())
is a valid expression of integer-like type ([iterator.concept.winc]),ranges::reserve_hint(E)
is expression-equivalent toauto(t.reserve_hint())
.[…]
When an exact size is available, providing an additional, less precise reserve_hint
serves no
practical
purpose.
There is no use case in the standard that requires these redundant
reserve_hint
members. Both ranges::to
and standard containers reserve capacity by
calling ranges::reserve_hint
, which already retrieves the best available size estimate.
Additionally, the motivating case in P2846
(uppercase_view
) provides only a reserve_hint
member, as it cannot offer an exact size:
template<input_range V> class uppercase_view { constexpr const V & base() const; constexpr auto begin() const; constexpr auto end() const; constexpr auto reserve_hint() requires approximately_sized_range<V> { return ranges::reserve_hint(base()); } constexpr auto reserve_hint() const requires approximately_sized_range<const V> { return ranges::reserve_hint(base()); } };
This indicates that reserve_hint
should only be used to resolve situations where
size
cannot be provided, as
it is only reasonable to provide reserve_hint
if approximate_sized_range
is modeled
and sized_range
is not; otherwise these reserve_hint
would lead to unnecessary instantiation
overhead. We should not pay for what we do not need.
This wording is relative to the latest working draft.
It replaces approximately_sized_range
with the newly introduced exposition-only concept
only-approximately-sized
in all relevant view classes,
to remove reserve_hint
members from views that already provide a size
member.
Modify 25.7.5 [range.adaptor.helpers], as indicated:
namespace std::ranges { […] template<bool Const, class... Views> concept all-forward = // exposition only (forward_range<maybe-const<Const, Views>> && ...); template<class R> concept only-approximately-sized = // exposition only !sized_range<R> && approximately_sized_range<R>; }
Modify 25.7.6.2 [range.ref.view], as indicated:
namespace std::ranges { template<range R> requires is_object_v<R> class ref_view : public view_interface<ref_view<R>> { […] public: […] constexpr auto reserve_hint() const requiresapproximately_sized_rangeonly-approximately-sized<R> { return ranges::reserve_hint(*r_); } […] }; […] }
Modify 25.7.6.3 [range.owning.view], as indicated:
namespace std::ranges { template<range R> requires movable<R> && (!is-initializer-list<R>) // see [range.refinements] class owning_view : public view_interface<owning_view<R>> { […] public: […] constexpr auto reserve_hint() requiresapproximately_sized_rangeonly-approximately-sized<R> { return ranges::reserve_hint(r_); } constexpr auto reserve_hint() const requiresapproximately_sized_rangeonly-approximately-sized<const R> { return ranges::reserve_hint(r_); } […] }; }
Modify 25.7.7.2 [range.as.rvalue.view], as indicated:
namespace std::ranges { templatelt;view Vgt; requires input_rangelt;Vgt; class as_rvalue_view : public view_interfacelt;as_rvalue_viewlt;Vgt;gt; { […] public: […] constexpr auto reserve_hint() requiresapproximately_sized_rangeonly-approximately-sized<V> { return ranges::reserve_hint(base_); } constexpr auto reserve_hint() const requiresapproximately_sized_rangeonly-approximately-sized<const V> { return ranges::reserve_hint(base_); } }; […] }
Modify 25.7.9.2 [range.transform.view], as indicated:
namespace std::ranges { templatelt;input_range V, move_constructible F> requires viewlt;V> && is_object_vlt;F> && regular_invocablelt;F&, range_reference_tlt;V>> && can-referencelt;invoke_result_tlt;F&, range_reference_tlt;V>>> class transform_view : public view_interfacelt;transform_viewlt;V, F>> { […] public: […] constexpr auto reserve_hint() requiresapproximately_sized_rangeonly-approximately-sized<V> { return ranges::reserve_hint(base_); } constexpr auto reserve_hint() const requiresapproximately_sized_rangeonly-approximately-sized<const V> { return ranges::reserve_hint(base_); } }; […] }
Modify 25.7.10.2 [range.take.view], as indicated:
namespace std::ranges { template<view V> class take_view : public view_interface<take_view<V>> { […] public: […] constexpr auto reserve_hint() noexcept {if constexpr (approximately_sized_range<V>) { auto n = static_cast<range_difference_t<V>>(ranges::reserve_hint(base_)); return to-unsigned-like(ranges::min(n, count_)); }return to-unsigned-like(count_); } constexpr auto reserve_hint() requires only-approximately-sized<V> { auto n = static_cast<range_difference_t<V>>(ranges::reserve_hint(base_)); return to-unsigned-like(ranges::min(n, count_)); } constexpr auto reserve_hint() const noexcept {if constexpr (approximately_sized_range<const V>) { auto n = static_cast<range_difference_t<const V>>(ranges::reserve_hint(base_)); return to-unsigned-like(ranges::min(n, count_)); }return to-unsigned-like(count_); } constexpr auto reserve_hint() const requires only-approximately-sized<const V> { auto n = static_cast<range_difference_t<const V>>(ranges::reserve_hint(base_)); return to-unsigned-like(ranges::min(n, count_)); } }; […] }
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 reserve_hint() requiresapproximately_sized_rangeonly-approximately-sized<V> { const auto s = static_cast<range_difference_t<V>>(ranges::reserve_hint(base_)); return to-unsigned-like(s < count_ ? 0 : s - count_); } constexpr auto reserve_hint() const requiresapproximately_sized_rangeonly-approximately-sized<const V> { const auto s = static_cast<range_difference_t<const V>>(ranges::reserve_hint(base_)); return to-unsigned-like(s < count_ ? 0 : s - count_); } […] }; […] }
Modify 25.7.20.2 [range.common.view], as indicated:
namespace std::ranges { template<view V> requires (!common_range<V> && copyable<iterator_t<V>>) class common_view : public view_interface<common_view<V>> { […] public: […] constexpr auto reserve_hint() requiresapproximately_sized_rangeonly-approximately-sized<V> { return ranges::reserve_hint(base_); } constexpr auto reserve_hint() const requiresapproximately_sized_rangeonly-approximately-sized<const V> { return ranges::reserve_hint(base_); } }; […] }
Modify 25.7.21.2 [range.reverse.view], as indicated:
namespace std::ranges { template<view V> requires bidirectional_range<V> class reverse_view : public view_interface<reverse_view<V>> { […] public: […] constexpr auto reserve_hint() requiresapproximately_sized_rangeonly-approximately-sized<V> { return ranges::reserve_hint(base_); } constexpr auto reserve_hint() const requiresapproximately_sized_rangeonly-approximately-sized<const V> { return ranges::reserve_hint(base_); } }; […] }
Modify 25.7.22.2 [range.as.const.view], as indicated:
namespace std::ranges { template<view V> requires input_range<V> class as_const_view : public view_interface<as_const_view<V>> { […] public: […] constexpr auto reserve_hint() requiresapproximately_sized_rangeonly-approximately-sized<V> { return ranges::reserve_hint(base_); } constexpr auto reserve_hint() const requiresapproximately_sized_rangeonly-approximately-sized<const V> { return ranges::reserve_hint(base_); } }; […] }
Modify 25.7.23.2 [range.elements.view], as indicated:
namespace std::ranges { […] template<input_range V, size_t N> requires view<V> && has-tuple-element<range_value_t<V>, N> && has-tuple-element<remove_reference_t<range_reference_t<V>>, N> && returnable-element<range_reference_t<V>, N> class elements_view : public view_interface<elements_view<V, N>> { public: […] constexpr auto reserve_hint() requiresapproximately_sized_rangeonly-approximately-sized<V> { return ranges::reserve_hint(base_); } constexpr auto reserve_hint() const requiresapproximately_sized_rangeonly-approximately-sized<const V> { return ranges::reserve_hint(base_); } […] }; }
Modify 25.7.24.2 [range.enumerate.view], as indicated:
namespace std::ranges { template<view V> requires range-with-movable-references<V> class enumerate_view : public view_interface<enumerate_view<V>> { […] public: […] constexpr auto reserve_hint() requiresapproximately_sized_rangeonly-approximately-sized<V> { return ranges::reserve_hint(base_); } constexpr auto reserve_hint() const requiresapproximately_sized_rangeonly-approximately-sized<const V> { return ranges::reserve_hint(base_); } […] }; […] }
Modify 25.7.27.2 [range.adjacent.view], as indicated:
namespace std::ranges { template<forward_range V, size_t N> requires view<V> && (N > 0) class adjacent_view : public view_interface<adjacent_view<V, N>> { […] public: […] constexpr auto reserve_hint() requires[…]approximately_sized_rangeonly-approximately-sized<V>; constexpr auto reserve_hint() const requiresapproximately_sized_rangeonly-approximately-sized<const V>; }; }constexpr auto reserve_hint() requiresapproximately_sized_rangeonly-approximately-sized<V>; constexpr auto reserve_hint() const requiresapproximately_sized_rangeonly-approximately-sized<const V>;-3- Effects: Equivalent to:
[…]
Modify 25.7.28.2 [range.adjacent.transform.view], as indicated:
namespace std::ranges { template<forward_range V, move_constructible F, size_t N> requires view<V> && (N > 0) && is_object_v<F> && regular_invocable<F&, REPEAT(range_reference_t<V>, N)...> && can-reference<invoke_result_t<F&, REPEAT(range_reference_t<V>, N)...>> class adjacent_transform_view : public view_interface<adjacent_transform_view<V, F, N>> { […] public: […] constexpr auto reserve_hint() requiresapproximately_sized_rangeonly-approximately-sized<InnerView> { return inner_.reserve_hint(); } constexpr auto reserve_hint() const requiresapproximately_sized_rangeonly-approximately-sized<const InnerView> { return inner_.reserve_hint(); } }; }
Modify 25.7.29.2 [range.chunk.view.input], as indicated:
namespace std::ranges { […] template<view V> requires input_range<V> class chunk_view : public view_interface<chunk_view<V, N>> { […] public: […] constexpr auto reserve_hint() requires[…]approximately_sized_rangeonly-approximately-sized<V>; constexpr auto reserve_hint() const requiresapproximately_sized_rangeonly-approximately-sized<const V>; }; […] }constexpr auto reserve_hint() requiresapproximately_sized_rangeonly-approximately-sized<V>; constexpr auto reserve_hint() const requiresapproximately_sized_rangeonly-approximately-sized<const V>;-6- Effects: Equivalent to:
[…]
Modify 25.7.29.2 [range.chunk.outer.value], as indicated:
namespace std::ranges { […] template<view V> requires input_range<V> struct chunk_view<V>::outer-iterator::value_type : view_interface<value_type> { […] public: […] constexpr auto size() const requires sized_sentinel_for<sentinel_t<V>, iterator_t<V>>; constexpr auto reserve_hint() const noexcept requires !sized_sentinel_for<sentinel_t<V>, iterator_t<V>>; }; }[…]constexpr auto reserve_hint() const noexcept requires !sized_sentinel_for<sentinel_t<V>, iterator_t<V>>;-5- Effects: Equivalent to:
[…]
Modify 25.7.29.6 [range.chunk.view.fwd], as indicated:
namespace std::ranges { template<view V> requires forward_range<V> class chunk_view : public view_interface<chunk_view<V, N>> { […] public: […] constexpr auto reserve_hint() requires[…]approximately_sized_rangeonly-approximately-sized<V>; constexpr auto reserve_hint() const requiresapproximately_sized_rangeonly-approximately-sized<const V>; }; }constexpr auto reserve_hint() requiresapproximately_sized_rangeonly-approximately-sized<V>; constexpr auto reserve_hint() const requiresapproximately_sized_rangeonly-approximately-sized<const V>;-4- Effects: Equivalent to:
[…]
Modify 25.7.30.2 [range.slide.view], as indicated:
namespace std::ranges { […] template<forward_range V> requires view<V> class slide_view : public view_interface<slide_view<V, N>> { […] public: […] constexpr auto reserve_hint() requires[…]approximately_sized_rangeonly-approximately-sized<V>; constexpr auto reserve_hint() const requiresapproximately_sized_rangeonly-approximately-sized<const V>; }; […] }constexpr auto reserve_hint() requiresapproximately_sized_rangeonly-approximately-sized<V>; constexpr auto reserve_hint() const requiresapproximately_sized_rangeonly-approximately-sized<const V>;-10- Effects: Equivalent to:
[…]
Modify 25.7.32.2 [range.stride.view], as indicated:
namespace std::ranges { […] template<input_range V> requires view<V> class stride_view : public view_interface<stride_view<V, N>> { […] public: […] constexpr auto reserve_hint() requires[…]approximately_sized_rangeonly-approximately-sized<V>; constexpr auto reserve_hint() const requiresapproximately_sized_rangeonly-approximately-sized<const V>; }; […] }constexpr auto reserve_hint() requiresapproximately_sized_rangeonly-approximately-sized<V>; constexpr auto reserve_hint() const requiresapproximately_sized_rangeonly-approximately-sized<const V>;-5- Effects: Equivalent to:
[…]
Modify 25.7.34.2 [range.cache.latest.view], as indicated:
namespace std::ranges { template<input_range V> requires view<V> class cache_latest_view : public view_interface<cache_latest_view<V, N>> { […] public: […] constexpr auto reserve_hint() requires[…]approximately_sized_rangeonly-approximately-sized<V>; constexpr auto reserve_hint() const requiresapproximately_sized_rangeonly-approximately-sized<const V>; }; […] }constexpr auto reserve_hint() requiresapproximately_sized_rangeonly-approximately-sized<V>; constexpr auto reserve_hint() const requiresapproximately_sized_rangeonly-approximately-sized<const V>;-5- Effects: Equivalent to:
[…]return ranges::reserve_hint(base_);
Modify 25.7.35.2 [range.to.input.view], as indicated:
namespace std::ranges { template<input_range V> requires view<V> class to_input_view : public view_interface<to_input_view<V, N>> { […] public: […] constexpr auto reserve_hint() requires[…]approximately_sized_rangeonly-approximately-sized<V>; constexpr auto reserve_hint() const requiresapproximately_sized_rangeonly-approximately-sized<const V>; }; […] }constexpr auto reserve_hint() requiresapproximately_sized_rangeonly-approximately-sized<V>; constexpr auto reserve_hint() const requiresapproximately_sized_rangeonly-approximately-sized<const V>;-6- Effects: Equivalent to:
return ranges::reserve_hint(base_);
reserve_hint
: Eagerly reserving memory for
not-quite-sized lazy ranges. URL: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p2846r6.pdf