On Tue, Jul 14, 2026 at 4:48 PM Yuao Ma <[email protected]> wrote:
> On Tue, Jul 14, 2026 at 10:20 PM Tomasz Kaminski <[email protected]> > wrote: > > > > > > > > On Tue, Jul 14, 2026 at 2:00 PM Patrick Palka <[email protected]> wrote: > >> > >> On Tue, 14 Jul 2026, Tomasz Kaminski wrote: > >> > >> > > >> > > >> > On Tue, Jul 14, 2026 at 6:32 AM Patrick Palka <[email protected]> > wrote: > >> > This patch defines a new utility function > std::__for_each_segment for > >> > iterating over "segmented" iterators, i.e. iterators for ranges > composed > >> > of sub-ranges. Such iterators must provide a static member > function > >> > _S_for_each_segment implementing traversal over their segments > via a > >> > callback function. This patch implements such traversal for > iterators > >> > of std::deque, ranges::join_view and ranges::concat_view. > >> > > >> > Could you explain what problems you see with this approach? > >> > I'm biased because that would be the interface I would use for > segmented > >> > iterators, and the I like how it end up being used in distance. > >> > In short I would be happy to merge this as the starting point. > >> > >> My main reservation with this approach is code duplication. > >> If we want a __for_each_segment_backward that traverses the segments in > >> reverse then I think we'll need to implement it from scratch for each > >> segmented iterator type. > > > > Indeed, but I couldn't think of standard algorithm that would need > backward > > iteration. > > > > fold_right{, _last} could use backward iteration, but it can also be > implemented with reverse iterators. > Is there a big benefit from iterating over segments, versus the whole range for fold? And other algorithms that visit all elements. I was thinking mostly about cases like distance (where we can compare iterators), or copy (when we could `memcpy` the segment). > > >> > >> Same if we want a version of > >> __for_each_segment whose callback returns void and never short circuits. > > > > That indeed is the case even with distance, but I assumed that the > benefit would > > not be worth the extra complexity. > > > >> > >> Same if we want a __for_each_segment whose __last is an arbitrary > >> sentinel instead of another iterator, I think? > > > > I think segmented iteration is only possible if the sentinel is `end()` > from > > the range. Otherwise, I think we need to reconstruct the combined > iterator > > after each element to compare it against the sentinel. Do you have > specific > > case in mind? > > > >> But when trying a different approach -- having an API in terms of more > >> fundamental primitives: > > > > There is interesting talk, about for_each being better primitive > operation > > (easier to compile, more optimal) for ranges when implementing a lot > > of algorithms. > >> > >> > >> template<typename _Iter> > >> struct __segmented_iterator_traits; > >> /* > >> { > >> using __outer_iter_it = ...; > >> - The iterator type of the outer range. > >> > >> using __inner_iter_t = ...; > >> - The iterator type of the inner range. > >> > >> static __outer_iter_t __outer_iter(_Iter __it); > >> - Return the outer iterator corresponding to __it. > >> »·······If the underlying range is not empty then the returned outer > iterator > >> »·······is always dereferenceable even if __it is the past-the-end > iterator. > >> > >> static __inner_iter_t __inner_iter(_Iter __it); > >> - Return the iterator into *__outer_it(__it) corresponding to > __it. > >> > >> static __inner_iter_t __begin(__outer_iter_t __it); > >> - Return the inner iterator corresponding to the beginning of > *__it. > >> > >> static __inner_iter_t __end(__outer_iter_t __it); > >> - Return the inner iterator corresponding to the end of *__it. > >> } > >> */ > >> > >> with which we could in turn generically define __for_each_segment and > >> __for_each_segment_backward functions, I ran into complications > >> with the fact that the join_view::end iterator's outer range is > >> not dereferencable (unlike deque::end). This needed > >> to be worked around by either adding a bool __is_end_iter(_Iter) > >> predicate to the API or by transparently transforming the > >> join_view::end() iterator from > >> {_M_outer=end(_M_base), _M_inner={}} > >> to > >> {_M_outer=prev(end(_M_base)), _M_inner=end(_M_outer)} > >> within the __segmented_iterator API for join_view so that > >> we could assume it's always derereferenceable. Both workarounds > >> seemed undesirable to me (the first complicates the API, the second > >> imposes an additional bidirectional constraint to join_view's > >> outer range). > >> > >> > >> And of course this more fundamental API would rule out iterators with > >> heteregeneous segments such as concat_view since it doesn't have a fixed > >> inner iterator type. > > > > Indeed, when thinking about the design space, I gravitated toward > `memcpy`. > > Concatenating vectors is something desirable. > >> > >> > >> So I think I prefer the current approach too, and live with the code > >> duplication when the time comes that we want additional versions of > >> __for_each_segment. > >> > >> > > >> > This patch defines > >> > > >> > PR libstdc++/123211 > >> > > >> > libstdc++-v3/ChangeLog: > >> > > >> > * include/bits/stl_deque.h > (_Deque_iterator::_S_for_each_segment): > >> > Define. > >> > (_Deque_iterator::_S_enable_for_each_segment): Define. > >> > * include/bits/stl_iterator_base_funcs.h: Include > <bits/move.h>. > >> > (__for_each_segment): Define. > >> > * include/bits/stl_iterator_base_types.h: Include > >> > <ext/type_traits.h> in C++98 mode. > >> > (__enable_for_each_segment): Define. > >> > (__segmented_iterator): Define in C++20. > >> > * include/std/ranges (join_view::_Iterator::_Iterator): > New > >> > constructor taking both an inner and outer iterator. > >> > (join_view::_Iterator::_S_for_each_segment): Define. > >> > (join_view::_Iterator::_S_enable_for_each_segment): > Define. > >> > (concat_view::_Iterator::_S_for_each_segment): Define. > >> > (concat_view::_Iterator::_S_enable_for_each_segment): > Define. > >> > * testsuite/23_containers/deque/for_each_segment.cc: > New test. > >> > * > testsuite/std/ranges/adaptors/join/for_each_segment.cc: New test. > >> > * testsuite/std/ranges/concat/for_each_segment.cc: New > test. > >> > --- > >> > libstdc++-v3/include/bits/stl_deque.h | 37 +++++++ > >> > .../include/bits/stl_iterator_base_funcs.h | 20 ++++ > >> > .../include/bits/stl_iterator_base_types.h | 22 ++++- > >> > libstdc++-v3/include/std/ranges | 99 > +++++++++++++++++++ > >> > .../23_containers/deque/for_each_segment.cc | 74 > ++++++++++++++ > >> > .../ranges/adaptors/join/for_each_segment.cc | 80 > +++++++++++++++ > >> > .../std/ranges/concat/for_each_segment.cc | 67 > +++++++++++++ > >> > 7 files changed, 398 insertions(+), 1 deletion(-) > >> > create mode 100644 > libstdc++-v3/testsuite/23_containers/deque/for_each_segment.cc > >> > create mode 100644 > libstdc++-v3/testsuite/std/ranges/adaptors/join/for_each_segment.cc > >> > create mode 100644 > libstdc++-v3/testsuite/std/ranges/concat/for_each_segment.cc > >> > > >> > diff --git a/libstdc++-v3/include/bits/stl_deque.h > b/libstdc++-v3/include/bits/stl_deque.h > >> > index 29a3f1b62896..b51342e1751d 100644 > >> > --- a/libstdc++-v3/include/bits/stl_deque.h > >> > +++ b/libstdc++-v3/include/bits/stl_deque.h > >> > @@ -417,6 +417,43 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER > >> > friend _Self > >> > operator+(difference_type __n, const _Self& __x) > _GLIBCXX_NOEXCEPT > >> > { return __x + __n; } > >> > + > >> > + template<typename _Fn> > >> > + static _Self > >> > + _S_for_each_segment(_Self __first, _Self __last, _Fn > __func) > >> > + { > >> > + if (__first._M_node == __last._M_node) > >> > + { > >> > + _Elt_pointer __ret = __func(__first._M_cur, > __last._M_cur); > >> > + if (__ret != __last._M_cur) > >> > + return _Self(__ret, __first._M_node); > >> > + return __last; > >> > + } > >> > + else > >> > + { > >> > + _Elt_pointer __ret = __func(__first._M_cur, > __first._M_last); > >> > + if (__ret != __first._M_last) > >> > + return _Self(__ret, __first._M_node); > >> > + > >> > + for (_Map_pointer __node = __first._M_node + 1; > >> > + __node < __last._M_node; > >> > + ++__node) > >> > + { > >> > + _Elt_pointer __end = *__node + > _S_buffer_size(); > >> > + __ret = __func(*__node, __end); > >> > + if (__ret != __end) > >> > + return _Self(__ret, __node); > >> > + } > >> > + > >> > + __ret = __func(__last._M_first, __last._M_cur); > >> > + if (__ret != __last._M_cur) > >> > + return _Self(__ret, __last._M_node); > >> > + > >> > + return __last; > >> > + } > >> > + } > >> > + > >> > + static const bool _S_enable_for_each_segment = true; > >> > }; > >> > > >> > /** > >> > diff --git > a/libstdc++-v3/include/bits/stl_iterator_base_funcs.h > b/libstdc++-v3/include/bits/stl_iterator_base_funcs.h > >> > index 2762090c6e48..0e992adfb005 100644 > >> > --- a/libstdc++-v3/include/bits/stl_iterator_base_funcs.h > >> > +++ b/libstdc++-v3/include/bits/stl_iterator_base_funcs.h > >> > @@ -66,6 +66,7 @@ > >> > #include <bits/concept_check.h> > >> > #include <debug/assertions.h> > >> > #include <bits/stl_iterator_base_types.h> > >> > +#include <bits/move.h> // For _GLIBCXX_MOVE > >> > > >> > namespace std _GLIBCXX_VISIBILITY(default) > >> > { > >> > @@ -383,6 +384,25 @@ namespace __detail > >> > #define _GLIBCXX_ITER_MOVE(__it) _GLIBCXX_MOVE(*__it) > >> > #endif > >> > > >> > + /* Mechanism for traversing ranges that are composed of > "segments" of other > >> > + ranges, such as std::deque and ranges::join_view. The > callback __func > >> > + is sequentially called on each constituent segment as a > pair of inner > >> > + iterators. If the callback returns something other than > the past-the-end > >> > + inner iterator, then the rest of the traversal gets > short-circuited and > >> > + returns the iterator at which we stopped. */ > >> > + template<typename _Iter, typename _Fn> > >> > +#if __cplusplus >= 201103L > >> > + __enable_if_t<__enable_for_each_segment<_Iter>::__value, > _Iter> > >> > +#else > >> > + _Iter > >> > +#endif > >> > + __for_each_segment(_Iter __first, _Iter __last, _Fn __func) > >> > + { > >> > + return _Iter::_S_for_each_segment(_GLIBCXX_MOVE(__first), > >> > + _GLIBCXX_MOVE(__last), > >> > + _GLIBCXX_MOVE(__func)); > >> > + } > >> > + > >> > /// @endcond > >> > > >> > _GLIBCXX_END_NAMESPACE_VERSION > >> > diff --git > a/libstdc++-v3/include/bits/stl_iterator_base_types.h > b/libstdc++-v3/include/bits/stl_iterator_base_types.h > >> > index 2366b5b5ce3e..21a482444383 100644 > >> > --- a/libstdc++-v3/include/bits/stl_iterator_base_types.h > >> > +++ b/libstdc++-v3/include/bits/stl_iterator_base_types.h > >> > @@ -66,7 +66,9 @@ > >> > #include <bits/c++config.h> > >> > > >> > #if __cplusplus >= 201103L > >> > -# include <type_traits> // For __void_t, is_convertible > >> > +# include <type_traits> // For __void_t, is_convertible, > __enable_if_t > >> > +#else > >> > +# include <ext/type_traits.h> // For __gnu_cxx::__enable_if > >> > #endif > >> > > >> > #if __cplusplus > 201703L && __cpp_concepts >= 201907L > >> > @@ -283,6 +285,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > >> > { enum { __value = > __is_base_of(random_access_iterator_tag, _Cat) }; }; > >> > #endif > >> > > >> > + template<typename _Iter, typename = void> > >> > + struct __enable_for_each_segment > >> > + { static const bool __value = false; }; > >> > + > >> > + template<typename _Iter> > >> > + struct __enable_for_each_segment<_Iter, > >> > +#if __cplusplus >= 201103L > >> > + __enable_if_t<_Iter::_S_enable_for_each_segment>> > >> > +#else > >> > + typename > __gnu_cxx::__enable_if<_Iter::_S_enable_for_each_segment, void>::__type> > >> > +#endif > >> > + { static const bool __value = true; }; > >> > + > >> > +#if __cpp_lib_concepts > >> > + template<typename _Iter> > >> > + concept __segmented_iterator = > __enable_for_each_segment<_Iter>::__value; > >> > +#endif > >> > + > >> > /// @endcond > >> > /// @} > >> > > >> > diff --git a/libstdc++-v3/include/std/ranges > b/libstdc++-v3/include/std/ranges > >> > index f89907d3e10e..a25a07f5d3dd 100644 > >> > --- a/libstdc++-v3/include/std/ranges > >> > +++ b/libstdc++-v3/include/std/ranges > >> > @@ -3149,6 +3149,12 @@ namespace views::__adaptor > >> > : _M_outer(std::move(__outer)), _M_parent(__parent) > >> > { _M_satisfy(); } > >> > > >> > + constexpr > >> > + _Iterator(_Parent* __parent, _Outer_iter __outer, > _Inner_iter __inner) > >> > + requires forward_range<_Base> > >> > + : _M_outer(std::move(__outer)), > _M_inner(std::move(__inner)), _M_parent(__parent) > >> > + { } > >> > + > >> > constexpr explicit > >> > _Iterator(_Parent* __parent) requires > (!forward_range<_Base>) > >> > : _M_parent(__parent) > >> > @@ -3272,6 +3278,50 @@ namespace views::__adaptor > >> > > >> > friend _Iterator<!_Const>; > >> > template<bool> friend struct _Sentinel; > >> > + > >> > + template<typename _Fn> > >> > + static constexpr _Iterator > >> > + _S_for_each_segment(_Iterator __first, _Iterator > __last, _Fn __func) > >> > + { > >> > + _Inner_iter __first_inner = > __first._M_get_inner(); > >> > + _Outer_iter __first_outer = > __first._M_get_outer(); > >> > + _Inner_iter __last_inner = __last._M_get_inner(); > >> > + _Outer_iter __last_outer = __last._M_get_outer(); > >> > + > >> > + if (__first_outer == __last_outer) > >> > + { > >> > + auto __ret = __func(__first_inner, > __last_inner); > >> > + return _Iterator(__first._M_parent, > __first_outer, __ret); > >> > + } > >> > + else > >> > + { > >> > + auto __end = ranges::end(*__first_outer); > >> > + auto __ret = __func(__first_inner, __end); > >> > + if (__ret != __end) > >> > + return _Iterator(__first._M_parent, > __first_outer, __ret); > >> > + > >> > + while (++__first_outer != __last_outer) > >> > + { > >> > + __end = ranges::end(*__first_outer); > >> > + __ret = > __func(ranges::begin(*__first_outer), __end); > >> > + if (__ret != __end) > >> > + return _Iterator(__first._M_parent, > __first_outer, __ret); > >> > + } > >> > + > >> > + if (__last != __last._M_parent->end()) > >> > + { > >> > + __end = __last_inner; > >> > + __ret = > __func(ranges::begin(*__last_outer), __end); > >> > + if (__ret != __end) > >> > + return _Iterator(__first._M_parent, > __first_outer, __ret); > >> > + } > >> > + > >> > + return __last; > >> > + } > >> > + } > >> > + > >> > + static constexpr bool _S_enable_for_each_segment > >> > + = forward_iterator<_Iterator>; > >> > }; > >> > > >> > template<bool _Const> > >> > @@ -10345,6 +10395,55 @@ namespace ranges > >> > ranges::swap(*__it1, *__it2); > >> > }, __x._M_it, __y._M_it); > >> > } > >> > + > >> > + template<typename _Fn> > >> > + static constexpr _Iterator > >> > + _S_for_each_segment(_Iterator __first, _Iterator __last, > _Fn __func) > >> > + { > >> > + return _S_invoke_with_runtime_index([&]<size_t _Ix>() > -> _Iterator { > >> > + return _S_invoke_with_runtime_index([&]<size_t _Iy>() > -> _Iterator { > >> > + if constexpr (_Ix == _Iy) > >> > + { > >> > + auto __ret = > __func(std::get<_Ix>(__first._M_it), std::get<_Iy>(__last._M_it)); > >> > + return _Iterator(__first._M_parent, > std::in_place_index<_Ix>, __ret); > >> > + } > >> > + else if constexpr (_Ix < _Iy) > >> > + { > >> > + auto __first_range = > std::get<_Ix>(__first._M_parent->_M_views); > >> > + auto __ret = > __func(std::get<_Ix>(__first._M_it), > >> > + ranges::end(__first_range)); > >> > + if (__ret != ranges::end(__first_range)) > >> > + return _Iterator(__first._M_parent, > std::in_place_index<_Ix>, __ret); > >> > + > >> > + return [&]<size_t _Idx = _Ix + 1>(this auto&& > __self) { > >> > + if constexpr (_Idx < _Iy) > >> > + { > >> > + auto __mid_range = > std::get<_Idx>(__first._M_parent->_M_views); > >> > + auto __mid_ret = > __func(ranges::begin(__mid_range), > >> > + > ranges::end(__mid_range)); > >> > + if (__mid_ret != ranges::end(__mid_range)) > >> > + return _Iterator(__first._M_parent, > std::in_place_index<_Idx>, __mid_ret); > >> > + return __self.template operator()<_Idx + > 1>(); > >> > + } > >> > + else > >> > + { > >> > + auto __last_range = > std::get<_Iy>(__last._M_parent->_M_views); > >> > + auto __last_ret = > __func(ranges::begin(__last_range), > >> > + > std::get<_Iy>(__last._M_it)); > >> > + return _Iterator(__first._M_parent, > std::in_place_index<_Iy>, __last_ret); > >> > + } > >> > + }(); > >> > + } > >> > + else > >> > + { > >> > + __builtin_unreachable(); > >> > + __glibcxx_assert(false); > >> > + } > >> > + }, __last._M_it.index()); > >> > + }, __first._M_it.index()); > >> > + } > >> > + > >> > + static constexpr bool _S_enable_for_each_segment = true; > >> > }; > >> > > >> > namespace views > >> > diff --git > a/libstdc++-v3/testsuite/23_containers/deque/for_each_segment.cc > b/libstdc++-v3/testsuite/23_containers/deque/for_each_segment.cc > >> > new file mode 100644 > >> > index 000000000000..d6127b0f0e2c > >> > --- /dev/null > >> > +++ > b/libstdc++-v3/testsuite/23_containers/deque/for_each_segment.cc > >> > @@ -0,0 +1,74 @@ > >> > +// { dg-do run } > >> > + > >> > +#include <deque> > >> > +#include <algorithm> > >> > +#include <testsuite_hooks.h> > >> > + > >> > +struct Finder > >> > +{ > >> > + static int call_count; > >> > + > >> > + int target; > >> > + > >> > + explicit Finder(int t) : target(t) { } > >> > + > >> > + int* > >> > + operator()(int* first, int* last) const > >> > + { > >> > + ++call_count; > >> > + return std::find(first, last, target); > >> > + } > >> > +}; > >> > + > >> > +int Finder::call_count = 0; > >> > + > >> > +void > >> > +test01() > >> > +{ > >> > + // A deque of 500 ints spans ~4 internal nodes (128 elements > per node) > >> > + // Node 0: elements 0 - 127 > >> > + // Node 1: elements 128 - 255 > >> > + // Node 2: elements 256 - 383 > >> > + // Node 3: elements 384 - 499 > >> > + std::deque<int> d; > >> > + for (int i = 0; i < 500; ++i) > >> > + d.push_back(i); > >> > + > >> > + std::deque<int>::iterator it; > >> > + > >> > + Finder::call_count = 0; > >> > + it = std::__for_each_segment(d.begin(), d.begin() + 100, > Finder(50)); > >> > + VERIFY( it == d.begin() + 50 ); > >> > + VERIFY( Finder::call_count == 1 ); > >> > + > >> > + Finder::call_count = 0; > >> > + it = std::__for_each_segment(d.begin(), d.begin() + 100, > Finder(999)); > >> > + VERIFY( it == d.begin() + 100 ); > >> > + VERIFY( Finder::call_count == 1 ); > >> > + > >> > + Finder::call_count = 0; > >> > + it = std::__for_each_segment(d.begin(), d.end(), > Finder(100)); > >> > + VERIFY( it == d.begin() + 100 ); > >> > + VERIFY( Finder::call_count == 1 ); > >> > + > >> > + Finder::call_count = 0; > >> > + it = std::__for_each_segment(d.begin(), d.end(), > Finder(200)); > >> > + VERIFY( it == d.begin() + 200 ); > >> > + VERIFY( Finder::call_count == 2 ); > >> > + > >> > + Finder::call_count = 0; > >> > + it = std::__for_each_segment(d.begin(), d.end(), > Finder(450)); > >> > + VERIFY( it == d.begin() + 450 ); > >> > + VERIFY( Finder::call_count == 4 ); > >> > + > >> > + Finder::call_count = 0; > >> > + it = std::__for_each_segment(d.begin(), d.end(), > Finder(999)); > >> > + VERIFY( it == d.end() ); > >> > + VERIFY( Finder::call_count == 4 ); > >> > +} > >> > + > >> > +int > >> > +main() > >> > +{ > >> > + test01(); > >> > +} > >> > diff --git > a/libstdc++-v3/testsuite/std/ranges/adaptors/join/for_each_segment.cc > b/libstdc++-v3/testsuite/std/ranges/adaptors/join/for_each_segment.cc > >> > new file mode 100644 > >> > index 000000000000..aad00606c97f > >> > --- /dev/null > >> > +++ > b/libstdc++-v3/testsuite/std/ranges/adaptors/join/for_each_segment.cc > >> > @@ -0,0 +1,80 @@ > >> > +// { dg-do run { target c++20 } } > >> > + > >> > +#include <ranges> > >> > +#include <vector> > >> > +#include <algorithm> > >> > +#include <testsuite_hooks.h> > >> > + > >> > +namespace ranges = std::ranges; > >> > + > >> > +void > >> > +test01() > >> > +{ > >> > + // 4 discrete vectors serving as segments > >> > + std::vector<std::vector<int>> vec > >> > + = { {0, 1, 2}, // Segment 0 > >> > + {3, 4, 5}, // Segment 1 > >> > + {6, 7, 8}, // Segment 2 > >> > + {9, 10, 11} // Segment 3 > >> > + }; > >> > + > >> > + auto jv = vec | std::views::join; > >> > + std::__segmented_iterator auto begin = jv.begin(); > >> > + std::__segmented_iterator auto end = jv.end(); > >> > + > >> > + static int call_count = 0; > >> > + auto make_finder = [](int target) { > >> > + return [target](auto first, auto last) { > >> > + if (first != last) > >> > + ++call_count; > >> > + return std::find(first, last, target); > >> > + }; > >> > + }; > >> > + > >> > + call_count = 0; > >> > + auto it = std::__for_each_segment(ranges::next(begin, 3), > ranges::next(begin, 6), > >> > + make_finder(4)); > >> > + VERIFY( it == ranges::next(begin, 4) ); > >> > + VERIFY( call_count == 1 ); > >> > + > >> > + call_count = 0; > >> > + it = std::__for_each_segment(ranges::next(begin, 3), > ranges::next(begin, 6), > >> > + make_finder(99)); > >> > + VERIFY( it == ranges::next(begin, 6) ); > >> > + VERIFY( call_count == 1 ); > >> > + > >> > + call_count = 0; > >> > + it = std::__for_each_segment(ranges::next(begin, 1), > ranges::next(begin, 11), > >> > + make_finder(2)); > >> > + VERIFY( it == ranges::next(begin, 2) ); > >> > + VERIFY( call_count == 1 ); > >> > + > >> > + call_count = 0; > >> > + it = std::__for_each_segment(ranges::next(begin, 1), > ranges::next(begin, 11), > >> > + make_finder(7)); > >> > + VERIFY( it == ranges::next(begin, 7) ); > >> > + VERIFY( call_count == 3 ); > >> > + > >> > + call_count = 0; > >> > + it = std::__for_each_segment(ranges::next(begin, 1), > ranges::next(begin, 11), > >> > + make_finder(9)); > >> > + VERIFY( it == ranges::next(begin, 9) ); > >> > + VERIFY( call_count == 4 ); > >> > + > >> > + call_count = 0; > >> > + it = std::__for_each_segment(ranges::next(begin, 1), > ranges::next(begin, 11), > >> > + make_finder(99)); > >> > + VERIFY( it == ranges::next(begin, 11) ); > >> > + VERIFY( call_count == 4 ); > >> > + > >> > + call_count = 0; > >> > + it = std::__for_each_segment(ranges::next(begin, 1), end, > make_finder(99)); > >> > + VERIFY( it == end ); > >> > + VERIFY( call_count == 4 ); > >> > +} > >> > + > >> > +int > >> > +main() > >> > +{ > >> > + test01(); > >> > +} > >> > diff --git > a/libstdc++-v3/testsuite/std/ranges/concat/for_each_segment.cc > b/libstdc++-v3/testsuite/std/ranges/concat/for_each_segment.cc > >> > new file mode 100644 > >> > index 000000000000..a74372b50456 > >> > --- /dev/null > >> > +++ > b/libstdc++-v3/testsuite/std/ranges/concat/for_each_segment.cc > >> > @@ -0,0 +1,67 @@ > >> > +// { dg-do run { target c++26 } } > >> > + > >> > +#include <ranges> > >> > +#include <vector> > >> > +#include <algorithm> > >> > +#include <testsuite_hooks.h> > >> > + > >> > +namespace ranges = std::ranges; > >> > + > >> > +void > >> > +test01() > >> > +{ > >> > + // 4 discrete vectors serving as segments > >> > + std::vector<int> v0 = {0, 1, 2}; // Segment 0 > >> > + std::vector<int> v1 = {3, 4, 5}; // Segment 1 > >> > + std::vector<int> v2 = {6, 7, 8}; // Segment 2 > >> > + std::vector<int> v3 = {9, 10, 11}; // Segment 3 > >> > + > >> > + auto cv = std::views::concat(v0, v1, v2, v3); > >> > + std::__segmented_iterator auto begin = cv.begin(); > >> > + std::__segmented_iterator auto end = cv.end(); > >> > + > >> > + static int call_count = 0; > >> > + auto make_finder = [](int target) { > >> > + return [target](auto first, auto last) { > >> > + if (first != last) > >> > + ++call_count; > >> > + return std::find(first, last, target); > >> > + }; > >> > + }; > >> > + > >> > + call_count = 0; > >> > + auto it = std::__for_each_segment(begin + 3, begin + 6, > make_finder(4)); > >> > + VERIFY( it == begin + 4 ); > >> > + VERIFY( call_count == 1 ); > >> > + > >> > + call_count = 0; > >> > + it = std::__for_each_segment(begin + 3, begin + 6, > make_finder(99)); > >> > + VERIFY( it == begin + 6 ); > >> > + VERIFY( call_count == 1 ); > >> > + > >> > + call_count = 0; > >> > + it = std::__for_each_segment(begin + 1, begin + 11, > make_finder(2)); > >> > + VERIFY( it == begin + 2 ); > >> > + VERIFY( call_count == 1 ); > >> > + > >> > + call_count = 0; > >> > + it = std::__for_each_segment(begin + 1, begin + 11, > make_finder(7)); > >> > + VERIFY( it == begin + 7 ); > >> > + VERIFY( call_count == 3 ); > >> > + > >> > + call_count = 0; > >> > + it = std::__for_each_segment(begin + 1, begin + 11, > make_finder(9)); > >> > + VERIFY( it == begin + 9 ); > >> > + VERIFY( call_count == 4 ); > >> > + > >> > + call_count = 0; > >> > + it = std::__for_each_segment(begin + 1, end, > make_finder(99)); > >> > + VERIFY( it == end ); > >> > + VERIFY( call_count == 4 ); > >> > +} > >> > + > >> > +int > >> > +main() > >> > +{ > >> > + test01(); > >> > +} > >> > -- > >> > 2.55.0.141.g55526a1826 > >> > > >> > > >> > > >
