[gcc r16-8389] libstdc++: Implement P3725R3 Filter View Extensions for Safer Use
Patrick Palka
ppalka@gcc.gnu.org
Wed Apr 1 15:35:09 GMT 2026
https://gcc.gnu.org/g:d60b69a2eb798001a589c6de0f5fb9d25a5d0d9e
commit r16-8389-gd60b69a2eb798001a589c6de0f5fb9d25a5d0d9e
Author: Patrick Palka <ppalka@redhat.com>
Date: Wed Apr 1 11:34:24 2026 -0400
libstdc++: Implement P3725R3 Filter View Extensions for Safer Use
This implements the recently approved C++26 paper P3725R3, which
we treat as a Defect Report against C++20.
libstdc++-v3/ChangeLog:
* include/bits/version.def (ranges_filter): Define for C++20.
* include/bits/version.h: Regenerate.
* include/std/ranges: Provide __cpp_lib_ranges_filter.
(filter_view::_Iterator): Add _Const template parameter.
(filter_view::_Iterator::_S_iter_concept): Return
input_iterator_tag if _Const.
(filter_view::_Iterator::_Parent): New.
(filter_view::_Iterator::_Base): New.
(filter_view::_Iterator::_Vp_iter): Replace with ...
(filter_view::_Iterator::_Base_iter): ... this.
(filter_view::_Iterator::_M_current): Adjust to consider _Const.
(filter_view::_Iterator::_M_parent): Likewise.
(filter_view::_Iterator::value_type): Likewise.
(filter_view::_Iterator::difference_type): Likewise.
(filter_view::_Iterator::_Iterator): Likewise. Add
const-converting overload.
(filter_view::_Iterator::base): Adjust to consider _Const.
(filter_view::_Iterator::operator*): Likewise.
(filter_view::_Iterator::operator->): Likewise.
(filter_view::_Iterator::operator++): Likewise.
(filter_view::_Iterator::operator--): Likewise.
(filter_view::_Iterator::iter_move): Likewise.
(filter_view::_Iterator::iter_swap): Likewise.
(filter_view::_Sentinel): Add _Const template parameter.
(filter_view::_Sentinel::_Parent): New.
(filter_view::_Sentinel::_Base): New.
(filter_view::_Sentinel::_M_end): Adjust to consider _Const.
(filter_view::_Sentinel::_Sentinel): Likewise. Add
const-converting overload.
(filter_view::_Sentinel::base): Adjust to consider _Const.
(filter_view::_Sentinel::operator==): Likewise. Inline the
helper member function __equal.
(filter_view::begin): Adjust return type of non-const overload.
New const overload.
(filter_view::end): Likewise.
* testsuite/std/ranges/adaptors/filter.cc: Verify value of
__cpp_lib_ranges_filter.
(test08): New test.
Reviewed-by: Jonathan Wakely <jwakely@redhat.com>
Diff:
---
libstdc++-v3/include/bits/version.def | 9 ++
libstdc++-v3/include/bits/version.h | 10 ++
libstdc++-v3/include/std/ranges | 107 +++++++++++++++------
.../testsuite/std/ranges/adaptors/filter.cc | 38 +++++++-
4 files changed, 131 insertions(+), 33 deletions(-)
diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def
index 434d38c8fbdf..b7d4b3849acb 100644
--- a/libstdc++-v3/include/bits/version.def
+++ b/libstdc++-v3/include/bits/version.def
@@ -2181,6 +2181,15 @@ ftms = {
};
};
+ftms = {
+ name = ranges_filter;
+ values = {
+ // This is P3725R3, a C++26 change, but we treat it as a DR against C++20.
+ v = 202603;
+ cxxmin = 20;
+ };
+};
+
ftms = {
name = ranges_to_input;
values = {
diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h
index bb2475087f1f..c2219bedb5f8 100644
--- a/libstdc++-v3/include/bits/version.h
+++ b/libstdc++-v3/include/bits/version.h
@@ -2446,6 +2446,16 @@
#endif /* !defined(__cpp_lib_text_encoding) */
#undef __glibcxx_want_text_encoding
+#if !defined(__cpp_lib_ranges_filter)
+# if (__cplusplus >= 202002L)
+# define __glibcxx_ranges_filter 202603L
+# if defined(__glibcxx_want_all) || defined(__glibcxx_want_ranges_filter)
+# define __cpp_lib_ranges_filter 202603L
+# endif
+# endif
+#endif /* !defined(__cpp_lib_ranges_filter) */
+#undef __glibcxx_want_ranges_filter
+
#if !defined(__cpp_lib_ranges_to_input)
# if (__cplusplus > 202302L)
# define __glibcxx_ranges_to_input 202502L
diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges
index 95e0109a10cf..249cc466db4f 100644
--- a/libstdc++-v3/include/std/ranges
+++ b/libstdc++-v3/include/std/ranges
@@ -65,6 +65,7 @@
#define __glibcxx_want_ranges_chunk
#define __glibcxx_want_ranges_chunk_by
#define __glibcxx_want_ranges_enumerate
+#define __glibcxx_want_ranges_filter
#define __glibcxx_want_ranges_indices
#define __glibcxx_want_ranges_join_with
#define __glibcxx_want_ranges_repeat
@@ -1738,15 +1739,19 @@ namespace views::__adaptor
class filter_view : public view_interface<filter_view<_Vp, _Pred>>
{
private:
+ template<bool _Const>
struct _Sentinel;
+ template<bool _Const>
struct _Iterator : __detail::__filter_view_iter_cat<_Vp>
{
private:
static constexpr auto
_S_iter_concept()
{
- if constexpr (bidirectional_range<_Vp>)
+ if constexpr (_Const)
+ return input_iterator_tag{};
+ else if constexpr (bidirectional_range<_Vp>)
return bidirectional_iterator_tag{};
else if constexpr (forward_range<_Vp>)
return forward_iterator_tag{};
@@ -1755,42 +1760,52 @@ namespace views::__adaptor
}
friend filter_view;
+ friend _Iterator<!_Const>;
- using _Vp_iter = iterator_t<_Vp>;
+ using _Parent = __maybe_const_t<_Const, filter_view>;
+ using _Base = __maybe_const_t<_Const, _Vp>;
+ using _Base_iter = iterator_t<_Base>;
- _Vp_iter _M_current = _Vp_iter();
- filter_view* _M_parent = nullptr;
+ _Base_iter _M_current = _Base_iter();
+ _Parent* _M_parent = nullptr;
public:
using iterator_concept = decltype(_S_iter_concept());
// iterator_category defined in __filter_view_iter_cat
- using value_type = range_value_t<_Vp>;
- using difference_type = range_difference_t<_Vp>;
+ using value_type = range_value_t<_Base>;
+ using difference_type = range_difference_t<_Base>;
- _Iterator() requires default_initializable<_Vp_iter> = default;
+ _Iterator() requires default_initializable<_Base_iter> = default;
constexpr
- _Iterator(filter_view* __parent, _Vp_iter __current)
+ _Iterator(_Parent* __parent, _Base_iter __current)
: _M_current(std::move(__current)),
_M_parent(__parent)
{ }
- constexpr const _Vp_iter&
+ constexpr
+ _Iterator(_Iterator<!_Const> __i)
+ requires _Const && convertible_to<iterator_t<_Vp>, iterator_t<_Base>>
+ : _M_current(std::move(__i._M_current)),
+ _M_parent(std::move(__i._M_parent))
+ { }
+
+ constexpr const _Base_iter&
base() const & noexcept
{ return _M_current; }
- constexpr _Vp_iter
+ constexpr _Base_iter
base() &&
{ return std::move(_M_current); }
- constexpr range_reference_t<_Vp>
+ constexpr range_reference_t<_Base>
operator*() const
{ return *_M_current; }
- constexpr _Vp_iter
+ constexpr _Base_iter
operator->() const
- requires __detail::__has_arrow<_Vp_iter>
- && copyable<_Vp_iter>
+ requires __detail::__has_arrow<_Base_iter>
+ && copyable<_Base_iter>
{ return _M_current; }
constexpr _Iterator&
@@ -1807,7 +1822,7 @@ namespace views::__adaptor
{ ++*this; }
constexpr _Iterator
- operator++(int) requires forward_range<_Vp>
+ operator++(int) requires forward_range<_Base>
{
auto __tmp = *this;
++*this;
@@ -1815,7 +1830,7 @@ namespace views::__adaptor
}
constexpr _Iterator&
- operator--() requires bidirectional_range<_Vp>
+ operator--() requires bidirectional_range<_Base>
{
do
--_M_current;
@@ -1824,7 +1839,7 @@ namespace views::__adaptor
}
constexpr _Iterator
- operator--(int) requires bidirectional_range<_Vp>
+ operator--(int) requires bidirectional_range<_Base>
{
auto __tmp = *this;
--*this;
@@ -1833,10 +1848,10 @@ namespace views::__adaptor
friend constexpr bool
operator==(const _Iterator& __x, const _Iterator& __y)
- requires equality_comparable<_Vp_iter>
+ requires equality_comparable<_Base_iter>
{ return __x._M_current == __y._M_current; }
- friend constexpr range_rvalue_reference_t<_Vp>
+ friend constexpr range_rvalue_reference_t<_Base>
iter_move(const _Iterator& __i)
noexcept(noexcept(ranges::iter_move(__i._M_current)))
{ return ranges::iter_move(__i._M_current); }
@@ -1844,34 +1859,44 @@ namespace views::__adaptor
friend constexpr void
iter_swap(const _Iterator& __x, const _Iterator& __y)
noexcept(noexcept(ranges::iter_swap(__x._M_current, __y._M_current)))
- requires indirectly_swappable<_Vp_iter>
+ requires indirectly_swappable<_Base_iter>
{ ranges::iter_swap(__x._M_current, __y._M_current); }
};
+ template<bool _Const>
struct _Sentinel
{
private:
- sentinel_t<_Vp> _M_end = sentinel_t<_Vp>();
+ using _Parent = __maybe_const_t<_Const, filter_view>;
+ using _Base = __maybe_const_t<_Const, _Vp>;
+ sentinel_t<_Base> _M_end = sentinel_t<_Base>();
- constexpr bool
- __equal(const _Iterator& __i) const
- { return __i._M_current == _M_end; }
+ friend _Sentinel<!_Const>;
public:
_Sentinel() = default;
constexpr explicit
- _Sentinel(filter_view* __parent)
+ _Sentinel(_Parent* __parent)
: _M_end(ranges::end(__parent->_M_base))
{ }
- constexpr sentinel_t<_Vp>
+ constexpr
+ _Sentinel(_Sentinel<!_Const> __i)
+ requires _Const && convertible_to<sentinel_t<_Vp>, sentinel_t<_Base>>
+ : _M_end(std::move(__i._M_end))
+ { }
+
+ constexpr sentinel_t<_Base>
base() const
{ return _M_end; }
- friend constexpr bool
- operator==(const _Iterator& __x, const _Sentinel& __y)
- { return __y.__equal(__x); }
+ template<bool _Const2>
+ requires sentinel_for<sentinel_t<_Base>,
+ iterator_t<__maybe_const_t<_Const2, _Vp>>>
+ friend constexpr bool
+ operator==(const _Iterator<_Const2>& __x, const _Sentinel& __y)
+ { return __x._M_current == __y._M_end; }
};
_Vp _M_base = _Vp();
@@ -1900,7 +1925,7 @@ namespace views::__adaptor
pred() const
{ return *_M_pred; }
- constexpr _Iterator
+ constexpr _Iterator<false>
begin()
{
if (_M_cached_begin._M_has_value())
@@ -1914,14 +1939,32 @@ namespace views::__adaptor
return {this, std::move(__it)};
}
+ constexpr _Iterator<true>
+ begin() const
+ requires (input_range<const _Vp> && !forward_range<const _Vp>
+ && indirect_unary_predicate<const _Pred, iterator_t<const _Vp>>)
+ {
+ __glibcxx_assert(_M_pred.has_value());
+ auto __it = ranges::find_if(ranges::begin(_M_base),
+ ranges::end(_M_base),
+ std::ref(*_M_pred));
+ return {this, std::move(__it)};
+ }
+
constexpr auto
end()
{
if constexpr (common_range<_Vp>)
- return _Iterator{this, ranges::end(_M_base)};
+ return _Iterator<false>{this, ranges::end(_M_base)};
else
- return _Sentinel{this};
+ return _Sentinel<false>{this};
}
+
+ constexpr _Sentinel<true>
+ end() const
+ requires (input_range<const _Vp> && !forward_range<const _Vp>
+ && indirect_unary_predicate<const _Pred, iterator_t<const _Vp>>)
+ { return _Sentinel<true>{this}; }
};
template<typename _Range, typename _Pred>
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/filter.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/filter.cc
index 7595e43d4e60..5b1e1ad05bc2 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/filter.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/filter.cc
@@ -17,12 +17,18 @@
// { dg-do run { target c++20 } }
-#include <algorithm>
#include <ranges>
+
+#if __cpp_lib_ranges_filter != 202603L
+# error "Feature-test macro for ranges_filter has wrong value in <ranges>."
+#endif
+
+#include <algorithm>
#include <testsuite_hooks.h>
#include <testsuite_iterators.h>
using __gnu_test::test_range;
+using __gnu_test::input_iterator_wrapper;
using __gnu_test::bidirectional_iterator_wrapper;
using __gnu_test::forward_iterator_wrapper;
using __gnu_test::random_access_iterator_wrapper;
@@ -166,6 +172,35 @@ test07()
static_assert( test07() );
+void
+test08()
+{
+ // P3725R3 Filter View Extensions for Safer Use
+ int x[] = {1,2,3,4,5,6};
+ __gnu_test::test_range<int, input_iterator_wrapper> rx(x);
+ auto v = rx | views::filter([](int i) { return (i % 2) == 0; });
+ using R = decltype(v);
+ static_assert( ranges::input_range<R> && !ranges::forward_range<R> );
+ static_assert( ranges::input_range<const R> && !ranges::forward_range<const R> );
+ const auto& cv = v;
+ auto it = v.begin();
+ decltype(cv.begin()) cit = it;
+ auto sent = v.end();
+ decltype(cv.end()) csent = sent;
+ VERIFY( cit == cit && it == cit && cit == it );
+ VERIFY( cit != csent && it != csent && cit != sent );
+ VERIFY( ranges::equal(cv, (int[]){2,4,6}) );
+
+ [](auto&& rx) {
+ // filter_view of forward+ range is still not const-iterable.
+ static_assert( ranges::forward_range<decltype(rx)> );
+ auto v = rx | views::filter([](int) { return true; });
+ using R = decltype(v);
+ static_assert( ranges::forward_range<R> );
+ static_assert( ! ranges::range<const R> );
+ }(__gnu_test::test_range<int, forward_iterator_wrapper>(x));
+}
+
int
main()
{
@@ -177,4 +212,5 @@ main()
test05<random_access_iterator_wrapper>();
test06();
test07();
+ test08();
}
More information about the Libstdc++-cvs
mailing list