| 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
Ewith 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 requires approximately_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() requires approximately_sized_rangeonly-approximately-sized<R>
{ return ranges::reserve_hint(r_); }
constexpr auto reserve_hint() const requires approximately_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() requires approximately_sized_rangeonly-approximately-sized<V>
{ return ranges::reserve_hint(base_); }
constexpr auto reserve_hint() const requires approximately_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() requires approximately_sized_rangeonly-approximately-sized<V>
{ return ranges::reserve_hint(base_); }
constexpr auto reserve_hint() const requires approximately_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() requires approximately_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 requires approximately_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() requires approximately_sized_rangeonly-approximately-sized<V> {
return ranges::reserve_hint(base_);
}
constexpr auto reserve_hint() const requires approximately_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() requires approximately_sized_rangeonly-approximately-sized<V> {
return ranges::reserve_hint(base_);
}
constexpr auto reserve_hint() const requires approximately_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() requires approximately_sized_rangeonly-approximately-sized<V> {
return ranges::reserve_hint(base_);
}
constexpr auto reserve_hint() const requires approximately_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() requires approximately_sized_rangeonly-approximately-sized<V>
{ return ranges::reserve_hint(base_); }
constexpr auto reserve_hint() const requires approximately_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() requires approximately_sized_rangeonly-approximately-sized<V>
{ return ranges::reserve_hint(base_); }
constexpr auto reserve_hint() const requires approximately_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() requires approximately_sized_rangeonly-approximately-sized<InnerView> {
return inner_.reserve_hint();
}
constexpr auto reserve_hint() const requires approximately_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