On Tue, 25 Nov 2025, Tomasz Kaminski wrote:

> 
> 
> On Tue, Nov 25, 2025 at 3:23 PM Patrick Palka <[email protected]> wrote:
>       On Tue, 25 Nov 2025, Tomasz Kaminski wrote:
> 
>       >
>       >
>       > On Tue, Nov 25, 2025 at 3:07 PM Patrick Palka <[email protected]> 
> wrote:
>       >       On Tue, 25 Nov 2025, Tomasz Kaminski wrote:
>       >
>       >       >
>       >       >
>       >       > On Tue, Nov 25, 2025 at 2:58 PM Patrick Palka 
> <[email protected]> wrote:
>       >       >       On Tue, 25 Nov 2025, Tomasz Kaminski wrote:
>       >       >
>       >       >       >
>       >       >       >
>       >       >       > On Tue, Nov 25, 2025 at 2:28 PM Patrick Palka 
> <[email protected]> wrote:
>       >       >       >       changes in v4: define the new helpers in 
> stl_iterator_base_funcs.h
>       >       >       >       rather than stl_iterator_base_types.h.  also 
> define an __iter_move_val
>       >       >       >       helper
>       >       >       >
>       >       >       >       changes v3: add inclusive_scan test using zip's 
> proxy iterator. rename
>       >       >       >       tests from p2408r5.cc to c++20_iter.cc
>       >       >       >
>       >       >       >       changes v2: more tests, and the one-argument 
> version of
>       >       >       >       __iter_concept_or_category is now constexpr 
> instead of
>       >       >       >       consteval
>       >       >       >
>       >       >       >       -- >8 --
>       >       >       >
>       >       >       >       From the paper's abstract:
>       >       >       >
>       >       >       >         Change the iterator requirements for 
> non-Ranges algorithms. For
>       >       >       >         forward iterators and above that are constant 
> iterators, instead of
>       >       >       >         requiring that iterators meet certain 
> Cpp17...Iterator requirements,
>       >       >       >         require that the iterators model certain 
> iterator concepts. This makes
>       >       >       >         iterators from several standard views usable 
> with non-Ranges
>       >       >       >         algorithms that require forward iterators or 
> above, such as the
>       >       >       >         parallel overloads of most algorithms.
>       >       >       >
>       >       >       >       This patch narrowly implements P2408R5 in C++23 
> mode and C++20 mode
>       >       >       >       (as an extension).  "Narrowly" because just as 
> in the paper, we don't
>       >       >       >       attempt to relax the requirements of mutable 
> iterators even though it's
>       >       >       >       possible in theory.  Note that the PSTL 
> algorithm requirements have
>       >       >       >       already been relaxed in r15-3650.  And we don't 
> bother touching the
>       >       >       >       deprecated parallel mode algorithms under 
> include/parallel.
>       >       >       >
>       >       >       >       The main workhorse of this paper is a new helper
>       >       >       >       __iterator_concept_or_category that replaces 
> existing uses of
>       >       >       >       __iterator_category and 
> iterator_traits::iterator_category with constant
>       >       >       >       iterators.  This new helper considers both the 
> iterator_concept and
>       >       >       >       iterator_category of the given iterator and 
> returns the former if it's
>       >       >       >       at least as strong as the latter.  It's 
> implemented in terms of the
>       >       >       >       __promotable_iterator concept added in r16-2588 
> that made std::advance
>       >       >       >       etc aware of C++20 iterators.  Note that this 
> helper doesn't check the
>       >       >       >       actual C++20 iterator concepts (which check 
> syntactic requirements along
>       >       >       >       with iterator_concept if it's defined) and 
> instead just checks for, and
>       >       >       >       fully trusts, the iterator_concept defined by 
> the iterator type.  This
>       >       >       >       is a slight deviation from the paper but IMHO 
> it's consistent with the
>       >       >       >       existing trusting of iterator_category and 
> should be good enough in
>       >       >       >       practice, though it means C++20 iterators that 
> don't define
>       >       >       >       iterator_concept will not be recognized as such 
> by this helper even if
>       >       >       >       they otherwise model the std::foo_iterator 
> concept.  (An undefined
>       >       >       >       iterator_concept effectively defaults to 
> random_access_iterator_tag.)
>       >       >       >
>       >       >       >       Most of the changes made here are effectively 
> optimizations that don't
>       >       >       >       have a semantic impact, e.g. for std::reduce.  
> I added tests for a
>       >       >       >       couple of algorithms where these changes are 
> observable.
>       >       >       >
>       >       >       >       The new __iterator_concept_or_category helper 
> can probably also be used
>       >       >       >       to fix PR100070 "Standard library container 
> iterator-pair constructors
>       >       >       >       should check C++20 iterator concepts".
>       >       >       >
>       >       >       >       As a follow-up to this patch we should either 
> remove the Boost-style
>       >       >       >       concept checks, or relax them accordingly.  It 
> seems we're leaning
>       >       >       >       towards removing them outright; see this thread:
>       >       >       >       
> https://gcc.gnu.org/pipermail/libstdc++/2025-May/061568.html
>       >       >       >
>       >       >       >       As suggested by Tomasz, this patch also 
> introduces an __iter_move_val
>       >       >       >       wrapper around ranges::iter_move that also 
> converts to the iterator's
>       >       >       >       value type and is usable before C++20 as well.
>       >       >       >
>       >       >       >               PR libstdc++/113299
>       >       >       >
>       >       >       >       libstdc++-v3/ChangeLog:
>       >       >       >
>       >       >       >               * include/bits/deque.tcc 
> (__copy_move_a1): Constrain with
>       >       >       >               __is_any_random_access_iter instead of 
> __is_random_access_iter.
>       >       >       >               (__copy_move_backward_a1): Likewise.
>       >       >       >               (__equal_aux1): Likewise.
>       >       >       >               * include/bits/stl_algo.h (__search_n): 
> Use
>       >       >       >               __iter_concept_or_category instead of 
> __iterator_category
>       >       >       >               or iterator_traits::iterator_category.
>       >       >       >               (find_end): Likewise.
>       >       >       >               (__is_permutation): Likewise.
>       >       >       >               (for_each_n): Likewise.
>       >       >       >               (unique_copy): Likewise, for constant 
> iterators.
>       >       >       >               (sample): Likewise, for constant 
> iterators.
>       >       >       >               * include/bits/stl_algobase.h 
> (__copy_move_a1): Adjust
>       >       >       >               deque-based forward declaration 
> accordingly.
>       >       >       >               (__copy_move_backward_a1): Likewise.
>       >       >       >               (__equal_aux1): Likewise.
>       >       >       >               (__lexicographical_compare_impl): Use
>       >       >       >               __iter_concept_or_category instead of 
> __iterator_category or
>       >       >       >               iterator_traits::iterator_category.
>       >       >       >               (__equal4): Likewise.
>       >       >       >               * include/bits/stl_iterator_base_funcs.h
>       >       >       >               (__iter_concept_or_category): New.
>       >       >       >               (__is_any_random_access_iter): New.
>       >       >       >               (__iter_move_val): New.
>       >       >       >               * include/bits/stl_uninitialized.h 
> (uninitialized_copy_n):
>       >       >       >               Use __iterator_concept_or_category 
> instead of
>       >       >       >               __iterator_category for the constant 
> iterator __first.
>       >       >       >               (__uninitialized_copy_n_pair): Likewise.
>       >       >       >               * include/bits/version.def 
> (algorithm_iterator_requirements):
>       >       >       >               Define.
>       >       >       >               * include/bits/version.h: Regenerate.
>       >       >       >               * include/std/algorithm: Provide the FTM
>       >       >       >               
> __cpp_lib_algorithm_iterator_requirements.
>       >       >       >               * include/std/memory: Likewise.
>       >       >       >               * include/std/numeric: Likewise.
>       >       >       >               (reduce): Use 
> __is_any_random_access_iter instead of
>       >       >       >               __is_random_access_iter.
>       >       >       >               (transform_reduce): Likewise.
>       >       >       >               (inclusive_scan): Use __iter_move_val 
> instead of std::move.
>       >       >       >               * 
> testsuite/25_algorithms/find_end/c++20_iter.cc: New test.
>       >       >       >               * 
> testsuite/25_algorithms/sample/c++20_iter.cc: New test.
>       >       >       >               * 
> testsuite/25_algorithms/search_n/c++20_iter.cc: New test.
>       >       >       >               * 
> testsuite/25_algorithms/unique_copy/c++20_iter.cc: New test.
>       >       >       >               * 
> testsuite/26_numerics/inclusive_scan/c++20_iter.cc: New test.
>       >       >       >       ---
>       >       >       >        libstdc++-v3/include/bits/deque.tcc           
> |  8 +--
>       >       >       >        libstdc++-v3/include/bits/stl_algo.h          
> | 28 ++++----
>       >       >       >        libstdc++-v3/include/bits/stl_algobase.h      
> | 20 +++---
>       >       >       >        .../include/bits/stl_iterator_base_funcs.h    
> | 70 +++++++++++++++++++
>       >       >       >        libstdc++-v3/include/bits/stl_uninitialized.h 
> |  4 +-
>       >       >       >        libstdc++-v3/include/bits/version.def         
> |  9 +++
>       >       >       >        libstdc++-v3/include/bits/version.h           
> | 10 +++
>       >       >       >        libstdc++-v3/include/std/algorithm            
> |  1 +
>       >       >       >        libstdc++-v3/include/std/memory               
> |  1 +
>       >       >       >        libstdc++-v3/include/std/numeric              
> | 11 +--
>       >       >       >        .../25_algorithms/find_end/c++20_iter.cc      
> | 23 ++++++
>       >       >       >        .../25_algorithms/sample/c++20_iter.cc        
> | 23 ++++++
>       >       >       >        .../25_algorithms/search_n/c++20_iter.cc      
> | 21 ++++++
>       >       >       >        .../25_algorithms/unique_copy/c++20_iter.cc   
> | 33 +++++++++
>       >       >       >        .../26_numerics/inclusive_scan/c++20_iter.cc  
> | 26 +++++++
>       >       >       >        15 files changed, 253 insertions(+), 35 
> deletions(-)
>       >       >       >        create mode 100644 
> libstdc++-v3/testsuite/25_algorithms/find_end/c++20_iter.cc
>       >       >       >        create mode 100644 
> libstdc++-v3/testsuite/25_algorithms/sample/c++20_iter.cc
>       >       >       >        create mode 100644 
> libstdc++-v3/testsuite/25_algorithms/search_n/c++20_iter.cc
>       >       >       >        create mode 100644 
> libstdc++-v3/testsuite/25_algorithms/unique_copy/c++20_iter.cc
>       >       >       >        create mode 100644 
> libstdc++-v3/testsuite/26_numerics/inclusive_scan/c++20_iter.cc
>       >       >       >
>       >       >       >       diff --git 
> a/libstdc++-v3/include/bits/deque.tcc b/libstdc++-v3/include/bits/deque.tcc
>       >       >       >       index 20b23fffc9e1..e409f3a3fc57 100644
>       >       >       >       --- a/libstdc++-v3/include/bits/deque.tcc
>       >       >       >       +++ b/libstdc++-v3/include/bits/deque.tcc
>       >       >       >       @@ -1225,7 +1225,7 @@ 
> _GLIBCXX_END_NAMESPACE_CONTAINER
>       >       >       >
>       >       >       >          template<bool _IsMove, typename _II, 
> typename _Tp>
>       >       >       >            typename __gnu_cxx::__enable_if<
>       >       >       >       -      __is_random_access_iter<_II>::__value,
>       >       >       >       +      
> __is_any_random_access_iter<_II>::__value,
>       >       >       >              _GLIBCXX_STD_C::_Deque_iterator<_Tp, 
> _Tp&, _Tp*> >::__type
>       >       >       >            __copy_move_a1(_II __first, _II __last,
>       >       >       >                          
> _GLIBCXX_STD_C::_Deque_iterator<_Tp, _Tp&, _Tp*> __result)
>       >       >       >       @@ -1347,7 +1347,7 @@ 
> _GLIBCXX_END_NAMESPACE_CONTAINER
>       >       >       >
>       >       >       >          template<bool _IsMove, typename _II, 
> typename _Tp>
>       >       >       >            typename __gnu_cxx::__enable_if<
>       >       >       >       -      __is_random_access_iter<_II>::__value,
>       >       >       >       +      
> __is_any_random_access_iter<_II>::__value,
>       >       >       >              _GLIBCXX_STD_C::_Deque_iterator<_Tp, 
> _Tp&, _Tp*> >::__type
>       >       >       >            __copy_move_backward_a1(_II __first, _II 
> __last,
>       >       >       >                       
> _GLIBCXX_STD_C::_Deque_iterator<_Tp, _Tp&, _Tp*> __result)
>       >       >       >       @@ -1406,7 +1406,7 @@ 
> _GLIBCXX_END_NAMESPACE_CONTAINER
>       >       >       >
>       >       >       >          template<typename _Tp, typename _Ref, 
> typename _Ptr, typename _II>
>       >       >       >            typename __gnu_cxx::__enable_if<
>       >       >       >       -      __is_random_access_iter<_II>::__value, 
> bool>::__type
>       >       >       >       +      
> __is_any_random_access_iter<_II>::__value, bool>::__type
>       >       >       >            
> __equal_aux1(_GLIBCXX_STD_C::_Deque_iterator<_Tp, _Ref, _Ptr> __first1,
>       >       >       >                        
> _GLIBCXX_STD_C::_Deque_iterator<_Tp, _Ref, _Ptr> __last1,
>       >       >       >                        _II __first2)
>       >       >       >       @@ -1422,7 +1422,7 @@ 
> _GLIBCXX_END_NAMESPACE_CONTAINER
>       >       >       >
>       >       >       >          template<typename _II, typename _Tp, 
> typename _Ref, typename _Ptr>
>       >       >       >            typename __gnu_cxx::__enable_if<
>       >       >       >       -      __is_random_access_iter<_II>::__value, 
> bool>::__type
>       >       >       >       +      
> __is_any_random_access_iter<_II>::__value, bool>::__type
>       >       >       >            __equal_aux1(_II __first1, _II __last1,
>       >       >       >                       
> _GLIBCXX_STD_C::_Deque_iterator<_Tp, _Ref, _Ptr> __first2)
>       >       >       >            {
>       >       >       >       diff --git 
> a/libstdc++-v3/include/bits/stl_algo.h b/libstdc++-v3/include/bits/stl_algo.h
>       >       >       >       index bbd1800af779..0d8f7af9d272 100644
>       >       >       >       --- a/libstdc++-v3/include/bits/stl_algo.h
>       >       >       >       +++ b/libstdc++-v3/include/bits/stl_algo.h
>       >       >       >       @@ -226,7 +226,7 @@ 
> _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       >       >       >               return std::__find_if(__first, __last, 
> __unary_pred);
>       >       >       >
>       >       >       >              return std::__search_n_aux(__first, 
> __last, __count, __unary_pred,
>       >       >       >       -                                
> std::__iterator_category(__first));
>       >       >       >       +                                
> std::__iter_concept_or_category(__first));
>       >       >       >            }
>       >       >       >
>       >       >       >          // find_end for forward iterators.
>       >       >       >       @@ -337,8 +337,8 @@ 
> _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       >       >       >              __glibcxx_requires_valid_range(__first2, 
> __last2);
>       >       >       >
>       >       >       >              return std::__find_end(__first1, 
> __last1, __first2, __last2,
>       >       >       >       -                            
> std::__iterator_category(__first1),
>       >       >       >       -                            
> std::__iterator_category(__first2),
>       >       >       >       +                            
> std::__iter_concept_or_category(__first1),
>       >       >       >       +                            
> std::__iter_concept_or_category(__first2),
>       >       >       >                                    
> __gnu_cxx::__ops::equal_to());
>       >       >       >            }
>       >       >       >
>       >       >       >       @@ -388,8 +388,8 @@ 
> _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       >       >       >              __glibcxx_requires_valid_range(__first2, 
> __last2);
>       >       >       >
>       >       >       >              return std::__find_end(__first1, 
> __last1, __first2, __last2,
>       >       >       >       -                            
> std::__iterator_category(__first1),
>       >       >       >       -                            
> std::__iterator_category(__first2),
>       >       >       >       +                            
> std::__iter_concept_or_category(__first1),
>       >       >       >       +                            
> std::__iter_concept_or_category(__first2),
>       >       >       >                                    __comp);
>       >       >       >            }
>       >       >       >
>       >       >       >       @@ -3495,9 +3495,9 @@ 
> _GLIBCXX_END_INLINE_ABI_NAMESPACE(_V2)
>       >       >       >                            _BinaryPredicate __pred)
>       >       >       >            {
>       >       >       >              using _Cat1
>       >       >       >       -       = typename 
> iterator_traits<_ForwardIterator1>::iterator_category;
>       >       >       >       +       = 
> __decltype(std::__iter_concept_or_category<_ForwardIterator1>());
>       >       >       >              using _Cat2
>       >       >       >       -       = typename 
> iterator_traits<_ForwardIterator2>::iterator_category;
>       >       >       >       +       = 
> __decltype(std::__iter_concept_or_category<_ForwardIterator2>());
>       >       >       >              using _It1_is_RA = is_same<_Cat1, 
> random_access_iterator_tag>;
>       >       >       >              using _It2_is_RA = is_same<_Cat2, 
> random_access_iterator_tag>;
>       >       >       >              constexpr bool __ra_iters = 
> __and_<_It1_is_RA, _It2_is_RA>::value;
>       >       >       >       @@ -3800,7 +3800,7 @@ 
> _GLIBCXX_BEGIN_NAMESPACE_ALGO
>       >       >       >            for_each_n(_InputIterator __first, _Size 
> __n, _Function __f)
>       >       >       >            {
>       >       >       >              auto __n2 = std::__size_to_integer(__n);
>       >       >       >       -      using _Cat = typename 
> iterator_traits<_InputIterator>::iterator_category;
>       >       >       >       +      using _Cat = 
> __decltype(std::__iter_concept_or_category<_InputIterator>());
>       >       >       >              if constexpr 
> (is_base_of_v<random_access_iterator_tag, _Cat>)
>       >       >       >               {
>       >       >       >                 if (__n2 <= 0)
>       >       >       >       @@ -4450,7 +4450,7 @@ 
> _GLIBCXX_BEGIN_NAMESPACE_ALGO
>       >       >       >               return __result;
>       >       >       >              return std::__unique_copy(__first, 
> __last, __result,
>       >       >       >                                       
> __gnu_cxx::__ops::equal_to(),
>       >       >       >       -                               
> std::__iterator_category(__first));
>       >       >       >       +                               
> std::__iter_concept_or_category(__first));
>       >       >       >            }
>       >       >       >
>       >       >       >          /**
>       >       >       >       @@ -4491,7 +4491,7 @@ 
> _GLIBCXX_BEGIN_NAMESPACE_ALGO
>       >       >       >              if (__first == __last)
>       >       >       >               return __result;
>       >       >       >              return std::__unique_copy(__first, 
> __last, __result, __binary_pred,
>       >       >       >       -                               
> std::__iterator_category(__first));
>       >       >       >       +                               
> std::__iter_concept_or_category(__first));
>       >       >       >            }
>       >       >       >
>       >       >       >        #if __cplusplus <= 201103L || 
> _GLIBCXX_USE_DEPRECATED
>       >       >       >       @@ -5881,10 +5881,10 @@ 
> _GLIBCXX_BEGIN_NAMESPACE_ALGO
>       >       >       >                  _SampleIterator __out, _Distance __n,
>       >       >       >                  _UniformRandomBitGenerator&& __g)
>       >       >       >            {
>       >       >       >       -      using __pop_cat = typename
>       >       >       >       -       
> std::iterator_traits<_PopulationIterator>::iterator_category;
>       >       >       >       -      using __samp_cat = typename
>       >       >       >       -       
> std::iterator_traits<_SampleIterator>::iterator_category;
>       >       >       >       +      using __pop_cat
>       >       >       >       +       = 
> __decltype(std::__iter_concept_or_category<_PopulationIterator>());
>       >       >       >       +      using __samp_cat
>       >       >       >       +       = typename 
> iterator_traits<_SampleIterator>::iterator_category;
>       >       >       >
>       >       >       >              static_assert(
>       >       >       >                 __or_<is_convertible<__pop_cat, 
> forward_iterator_tag>,
>       >       >       >       diff --git 
> a/libstdc++-v3/include/bits/stl_algobase.h 
> b/libstdc++-v3/include/bits/stl_algobase.h
>       >       >       >       index 443cbef76dee..701910afd837 100644
>       >       >       >       --- a/libstdc++-v3/include/bits/stl_algobase.h
>       >       >       >       +++ b/libstdc++-v3/include/bits/stl_algobase.h
>       >       >       >       @@ -480,7 +480,7 @@ 
> _GLIBCXX_END_NAMESPACE_CONTAINER
>       >       >       >
>       >       >       >          template<bool _IsMove, typename _II, 
> typename _Tp>
>       >       >       >            typename __gnu_cxx::__enable_if<
>       >       >       >       -      __is_random_access_iter<_II>::__value,
>       >       >       >       +      
> __is_any_random_access_iter<_II>::__value,
>       >       >       >              _GLIBCXX_STD_C::_Deque_iterator<_Tp, 
> _Tp&, _Tp*> >::__type
>       >       >       >            __copy_move_a1(_II, _II, 
> _GLIBCXX_STD_C::_Deque_iterator<_Tp, _Tp&, _Tp*>);
>       >       >       >
>       >       >       >       @@ -769,7 +769,7 @@ 
> _GLIBCXX_END_NAMESPACE_CONTAINER
>       >       >       >
>       >       >       >          template<bool _IsMove, typename _II, 
> typename _Tp>
>       >       >       >            typename __gnu_cxx::__enable_if<
>       >       >       >       -      __is_random_access_iter<_II>::__value,
>       >       >       >       +      
> __is_any_random_access_iter<_II>::__value,
>       >       >       >              _GLIBCXX_STD_C::_Deque_iterator<_Tp, 
> _Tp&, _Tp*> >::__type
>       >       >       >            __copy_move_backward_a1(_II, _II,
>       >       >       >                                   
> _GLIBCXX_STD_C::_Deque_iterator<_Tp, _Tp&, _Tp*>);
>       >       >       >       @@ -1219,7 +1219,7 @@ 
> _GLIBCXX_END_NAMESPACE_CONTAINER
>       >       >       >
>       >       >       >          template<typename _Tp, typename _Ref, 
> typename _Ptr, typename _II>
>       >       >       >            typename __gnu_cxx::__enable_if<
>       >       >       >       -      __is_random_access_iter<_II>::__value, 
> bool>::__type
>       >       >       >       +      
> __is_any_random_access_iter<_II>::__value, bool>::__type
>       >       >       >            
> __equal_aux1(_GLIBCXX_STD_C::_Deque_iterator<_Tp, _Ref, _Ptr>,
>       >       >       >                        
> _GLIBCXX_STD_C::_Deque_iterator<_Tp, _Ref, _Ptr>,
>       >       >       >                        _II);
>       >       >       >       @@ -1233,7 +1233,7 @@ 
> _GLIBCXX_END_NAMESPACE_CONTAINER
>       >       >       >
>       >       >       >          template<typename _II, typename _Tp, 
> typename _Ref, typename _Ptr>
>       >       >       >            typename __gnu_cxx::__enable_if<
>       >       >       >       -      __is_random_access_iter<_II>::__value, 
> bool>::__type
>       >       >       >       +      
> __is_any_random_access_iter<_II>::__value, bool>::__type
>       >       >       >            __equal_aux1(_II, _II,
>       >       >       >                       
> _GLIBCXX_STD_C::_Deque_iterator<_Tp, _Ref, _Ptr>);
>       >       >       >
>       >       >       >       @@ -1332,8 +1332,8 @@ 
> _GLIBCXX_END_NAMESPACE_CONTAINER
>       >       >       >                                          _II2 
> __first2, _II2 __last2,
>       >       >       >                                          _Compare 
> __comp)
>       >       >       >            {
>       >       >       >       -      typedef typename 
> iterator_traits<_II1>::iterator_category _Category1;
>       >       >       >       -      typedef typename 
> iterator_traits<_II2>::iterator_category _Category2;
>       >       >       >       +      typedef 
> __decltype(std::__iter_concept_or_category<_II1>()) _Category1;
>       >       >       >       +      typedef 
> __decltype(std::__iter_concept_or_category<_II2>()) _Category2;
>       >       >       >              typedef std::__lc_rai<_Category1, 
> _Category2> __rai_type;
>       >       >       >
>       >       >       >              __last1 = 
> __rai_type::__newlast1(__first1, __last1, __first2, __last2);
>       >       >       >       @@ -1649,8 +1649,8 @@ 
> _GLIBCXX_BEGIN_NAMESPACE_ALGO
>       >       >       >            __equal4(_II1 __first1, _II1 __last1, _II2 
> __first2, _II2 __last2)
>       >       >       >            {
>       >       >       >              using _RATag = 
> random_access_iterator_tag;
>       >       >       >       -      using _Cat1 = typename 
> iterator_traits<_II1>::iterator_category;
>       >       >       >       -      using _Cat2 = typename 
> iterator_traits<_II2>::iterator_category;
>       >       >       >       +      using _Cat1 = 
> __decltype(std::__iter_concept_or_category<_II1>());
>       >       >       >       +      using _Cat2 = 
> __decltype(std::__iter_concept_or_category<_II2>());
>       >       >       >              using _RAIters = __and_<is_same<_Cat1, 
> _RATag>, is_same<_Cat2, _RATag>>;
>       >       >       >              if constexpr (_RAIters::value)
>       >       >       >               {
>       >       >       >       @@ -1676,8 +1676,8 @@ 
> _GLIBCXX_BEGIN_NAMESPACE_ALGO
>       >       >       >                    _BinaryPredicate __binary_pred)
>       >       >       >            {
>       >       >       >              using _RATag = 
> random_access_iterator_tag;
>       >       >       >       -      using _Cat1 = typename 
> iterator_traits<_II1>::iterator_category;
>       >       >       >       -      using _Cat2 = typename 
> iterator_traits<_II2>::iterator_category;
>       >       >       >       +      using _Cat1 = 
> __decltype(std::__iter_concept_or_category<_II1>());
>       >       >       >       +      using _Cat2 = 
> __decltype(std::__iter_concept_or_category<_II2>());
>       >       >       >              using _RAIters = __and_<is_same<_Cat1, 
> _RATag>, is_same<_Cat2, _RATag>>;
>       >       >       >              if constexpr (_RAIters::value)
>       >       >       >               {
>       >       >       >       diff --git 
> a/libstdc++-v3/include/bits/stl_iterator_base_funcs.h 
> b/libstdc++-v3/include/bits/stl_iterator_base_funcs.h
>       >       >       >       index 7c80e1423e45..da5945727093 100644
>       >       >       >       --- 
> a/libstdc++-v3/include/bits/stl_iterator_base_funcs.h
>       >       >       >       +++ 
> b/libstdc++-v3/include/bits/stl_iterator_base_funcs.h
>       >       >       >       @@ -317,6 +317,76 @@ namespace __detail
>       >       >       >
>       >       >       >        #endif // C++11
>       >       >       >
>       >       >       >       +#if __glibcxx_algorithm_iterator_requirements 
> // C++ >= 20
>       >       >       >       +  template<typename _Iter>
>       >       >       >       +    consteval auto
>       >       >       >       +    __iter_concept_or_category()
>       >       >       >       +    {
>       >       >       >       +      if constexpr 
> (__detail::__promotable_iterator<_Iter>)
>       >       >       >       +       {
>       >       >       >       +         using __type = 
> __detail::__iter_traits<_Iter>::iterator_concept;
>       >       >       >       +         if constexpr (derived_from<__type, 
> random_access_iterator_tag>)
>       >       >       >       +           return random_access_iterator_tag{};
>       >       >       >       +         else
>       >       >       >       +           return __type{};
>       >       >       >       +       }
>       >       >       >       +      else
>       >       >       >       +       return typename 
> iterator_traits<_Iter>::iterator_category{};
>       >       >       >       +    }
>       >       >       >       +
>       >       >       >       +  template<typename _Iter>
>       >       >       >       +    __attribute__((__always_inline__))
>       >       >       >       +    constexpr auto
>       >       >       >       +    __iter_concept_or_category(const _Iter&)
>       >       >       >       +    { return 
> std::__iter_concept_or_category<_Iter>(); }
>       >       >       >       +#else
>       >       >       >       +  template<typename _Iter>
>       >       >       >       +    __attribute__((__always_inline__))
>       >       >       >       +    inline _GLIBCXX_CONSTEXPR
>       >       >       >       +    typename 
> iterator_traits<_Iter>::iterator_category
>       >       >       >       +    __iter_concept_or_category()
>       >       >       >       +    { return typename 
> iterator_traits<_Iter>::iterator_category(); }
>       >       >       >       +
>       >       >       >       +  template<typename _Iter>
>       >       >       >       +    __attribute__((__always_inline__))
>       >       >       >       +    inline _GLIBCXX_CONSTEXPR
>       >       >       >       +    typename 
> iterator_traits<_Iter>::iterator_category
>       >       >       >       +    __iter_concept_or_category(const _Iter&)
>       >       >       >       +    { return typename 
> iterator_traits<_Iter>::iterator_category(); }
>       >       >       >       +#endif
>       >       >       >       +
>       >       >       >       +#if __cplusplus >= 201103L
>       >       >       >       +  // Like __is_random_access_iter, but based 
> off of __iter_concept_or_category
>       >       >       >       +  // instead of 
> iterator_traits::iterator_category.
>       >       >       >       +  template<typename _Iter,
>       >       >       >       +          typename _Cat = 
> __decltype(__iter_concept_or_category<_Iter>())>
>       >       >       >       +    struct __is_any_random_access_iter
>       >       >       >       +      : is_base_of<random_access_iterator_tag, 
> _Cat>
>       >       >       >       +    { enum { __value = 
> __is_any_random_access_iter::value }; };
>       >       >       >       +#else
>       >       >       >       +  template<typename _Iter,
>       >       >       >       +          typename _Cat = 
> __decltype(__iter_concept_or_category<_Iter>())>
>       >       >       >       +    struct __is_any_random_access_iter
>       >       >       >       +    { enum { __value = 
> __is_base_of(random_access_iterator_tag, _Cat) }; };
>       >       >       >       +#endif
>       >       >       >       +
>       >       >       >       +#if __cplusplus >= 202002L
>       >       >       >       +  // A wrapper around ranges::iter_move that 
> also converts to the iterator's
>       >       >       >       +  // value type.
>       >       >       >       +  template<typename _Iter>
>       >       >       >       +    [[nodiscard]] constexpr iter_value_t<_Iter>
>       >       >       >       +    __iter_move_val(const _Iter& __it)
>       >       >       >       +    noexcept(noexcept(ranges::iter_move(__it)))
>       >       >       >
>       >       >       > We also need && 
> is_nothrow_cosntructible_v<iter_value_t<_Iter>, 
> iter_rvalue_reference_t<_Iter>>>,
>       >       >       > or just 
> noexcept(iter_value_t<_Iter>(ranges::iter_move(__it)))
>       >       >       >       +    { return ranges::iter_move(__it); }
>       >       >       >       +#else
>       >       >       >       +  template<typename _Iter>
>       >       >       >       +    _GLIBCXX_NODISCARD inline 
> _GLIBCXX_CONSTEXPR
>       >       >       >       +    typename iterator_traits<_Iter>::value_type
>       >       >       >       +    __iter_move_val(const _Iter& __it)
>       >       >       >       +    
> _GLIBCXX_NOEXCEPT_IF(noexcept(_GLIBCXX_MOVE(*__it)))
>       >       >       >
>       >       >       > Similar here, may have _Vt as the defaulted second 
> template parameter. 
>       >       >
>       >       >       Good catch.  Default template arguments on function 
> templates aren't
>       >       >       supported in C++98, so it seems cleanest to just have 
> three definitinons
>       >       >       of __iter_move_val:
>       >       >
>       >       > Thanks. LGTM now. 
>       >
>       >       Thanks for the review.
>       >
>       >       I noticed shortly after submitting this latest version that 
> using
>       >       __iter_move_val might introduce an extra copy in C++98 mode 
> compared to
>       >       using _GLIBCXX_MOVE directly, I think.  I wonder how much we 
> care?
>       >
>       > I would assume that this constructor would get elided anyway, so this 
> would be not
>       > observable. And we already require a copy constructor to compile. 
> 
>       Ah ok, makes sense.
> 
>       >       I guess we could defined __iter_move_val as a macro 
> (_GLIBCXX_ITER_MOVE)
>       >       to avoid this extra copy?
>       >
>       > This would reduce a lot of boilerplace (conditional noexcept), if we 
> define it as
>       > simply static_cast<iter_value_t>(ranges::iter_move), and there would 
> be only pre-C++20
>       > and post-C++20 versions.  
> 
>       The pre-C++20 macro would look pretty ugly:
> 
>       #define _GLIBCXX_ITER_MOVE(__it) \
>         typename 
> iterator_traits<__remove_cvref(__decltype(__it))>::value_type(_GLIBCXX_MOVE(*__it))
> 
> But we do not need the case to iterator_traits<...>::value_type pre-C++20. I 
> mean, 
> auto __x =  _GLIBCXX_ITER_MOVE(*__it);
> Will work correctly with just _GLIBCXX_MOVE(*__it), and such cast will add 
> the same additional elidable copy as we have currently.

Makes sense, like so then?

changes in v6: replace __iter_move_val with _GLIBCXX_ITER_MOVE macro

changes in v5: correct __iter_move_val's noexcept-spec

changes in v4: define the new helpers in stl_iterator_base_funcs.h
rather than stl_iterator_base_types.h.  also define an __iter_move_val
helper

changes v3: add inclusive_scan test using zip's proxy iterator. rename
tests from p2408r5.cc to c++20_iter.cc

changes v2: more tests, and the one-argument version of
__iter_concept_or_category is now constexpr instead of
consteval

-- >8 --

        PR libstdc++/113299

libstdc++-v3/ChangeLog:

        * include/bits/deque.tcc (__copy_move_a1): Constrain with
        __is_any_random_access_iter instead of __is_random_access_iter.
        (__copy_move_backward_a1): Likewise.
        (__equal_aux1): Likewise.
        * include/bits/stl_algo.h (__search_n): Use
        __iter_concept_or_category instead of __iterator_category
        or iterator_traits::iterator_category.
        (find_end): Likewise.
        (__is_permutation): Likewise.
        (for_each_n): Likewise.
        (unique_copy): Likewise, for constant iterators.
        (sample): Likewise, for constant iterators.
        * include/bits/stl_algobase.h (__copy_move_a1): Adjust
        deque-based forward declaration accordingly.
        (__copy_move_backward_a1): Likewise.
        (__equal_aux1): Likewise.
        (__lexicographical_compare_impl): Use
        __iter_concept_or_category instead of __iterator_category or
        iterator_traits::iterator_category.
        (__equal4): Likewise.
        * include/bits/stl_iterator_base_funcs.h
        (__iter_concept_or_category): New.
        (__is_any_random_access_iter): New.
        (_GLIBCXX_ITER_MOVE): New.
        * include/bits/stl_uninitialized.h (uninitialized_copy_n):
        Use __iterator_concept_or_category instead of
        __iterator_category for the constant iterator __first.
        (__uninitialized_copy_n_pair): Likewise.
        * include/bits/version.def (algorithm_iterator_requirements):
        Define.
        * include/bits/version.h: Regenerate.
        * include/std/algorithm: Provide the FTM
        __cpp_lib_algorithm_iterator_requirements.
        * include/std/memory: Likewise.
        * include/std/numeric: Likewise.
        (reduce): Use __is_any_random_access_iter instead of
        __is_random_access_iter.
        (transform_reduce): Likewise.
        (inclusive_scan): Use _GLIBCXX_ITER_MOVE instead of std::move.
        * testsuite/25_algorithms/find_end/c++20_iter.cc: New test.
        * testsuite/25_algorithms/sample/c++20_iter.cc: New test.
        * testsuite/25_algorithms/search_n/c++20_iter.cc: New test.
        * testsuite/25_algorithms/unique_copy/c++20_iter.cc: New test.
        * testsuite/26_numerics/inclusive_scan/c++20_iter.cc: New test.
---
 libstdc++-v3/include/bits/deque.tcc           |  8 +--
 libstdc++-v3/include/bits/stl_algo.h          | 28 ++++-----
 libstdc++-v3/include/bits/stl_algobase.h      | 20 +++---
 .../include/bits/stl_iterator_base_funcs.h    | 62 +++++++++++++++++++
 libstdc++-v3/include/bits/stl_uninitialized.h |  4 +-
 libstdc++-v3/include/bits/version.def         |  9 +++
 libstdc++-v3/include/bits/version.h           | 10 +++
 libstdc++-v3/include/std/algorithm            |  1 +
 libstdc++-v3/include/std/memory               |  1 +
 libstdc++-v3/include/std/numeric              | 11 ++--
 .../25_algorithms/find_end/c++20_iter.cc      | 23 +++++++
 .../25_algorithms/sample/c++20_iter.cc        | 23 +++++++
 .../25_algorithms/search_n/c++20_iter.cc      | 21 +++++++
 .../25_algorithms/unique_copy/c++20_iter.cc   | 33 ++++++++++
 .../26_numerics/inclusive_scan/c++20_iter.cc  | 26 ++++++++
 15 files changed, 245 insertions(+), 35 deletions(-)
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/find_end/c++20_iter.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/sample/c++20_iter.cc
 create mode 100644 libstdc++-v3/testsuite/25_algorithms/search_n/c++20_iter.cc
 create mode 100644 
libstdc++-v3/testsuite/25_algorithms/unique_copy/c++20_iter.cc
 create mode 100644 
libstdc++-v3/testsuite/26_numerics/inclusive_scan/c++20_iter.cc

diff --git a/libstdc++-v3/include/bits/deque.tcc 
b/libstdc++-v3/include/bits/deque.tcc
index 20b23fffc9e1..e409f3a3fc57 100644
--- a/libstdc++-v3/include/bits/deque.tcc
+++ b/libstdc++-v3/include/bits/deque.tcc
@@ -1225,7 +1225,7 @@ _GLIBCXX_END_NAMESPACE_CONTAINER
 
   template<bool _IsMove, typename _II, typename _Tp>
     typename __gnu_cxx::__enable_if<
-      __is_random_access_iter<_II>::__value,
+      __is_any_random_access_iter<_II>::__value,
       _GLIBCXX_STD_C::_Deque_iterator<_Tp, _Tp&, _Tp*> >::__type
     __copy_move_a1(_II __first, _II __last,
                   _GLIBCXX_STD_C::_Deque_iterator<_Tp, _Tp&, _Tp*> __result)
@@ -1347,7 +1347,7 @@ _GLIBCXX_END_NAMESPACE_CONTAINER
 
   template<bool _IsMove, typename _II, typename _Tp>
     typename __gnu_cxx::__enable_if<
-      __is_random_access_iter<_II>::__value,
+      __is_any_random_access_iter<_II>::__value,
       _GLIBCXX_STD_C::_Deque_iterator<_Tp, _Tp&, _Tp*> >::__type
     __copy_move_backward_a1(_II __first, _II __last,
                _GLIBCXX_STD_C::_Deque_iterator<_Tp, _Tp&, _Tp*> __result)
@@ -1406,7 +1406,7 @@ _GLIBCXX_END_NAMESPACE_CONTAINER
 
   template<typename _Tp, typename _Ref, typename _Ptr, typename _II>
     typename __gnu_cxx::__enable_if<
-      __is_random_access_iter<_II>::__value, bool>::__type
+      __is_any_random_access_iter<_II>::__value, bool>::__type
     __equal_aux1(_GLIBCXX_STD_C::_Deque_iterator<_Tp, _Ref, _Ptr> __first1,
                 _GLIBCXX_STD_C::_Deque_iterator<_Tp, _Ref, _Ptr> __last1,
                 _II __first2)
@@ -1422,7 +1422,7 @@ _GLIBCXX_END_NAMESPACE_CONTAINER
 
   template<typename _II, typename _Tp, typename _Ref, typename _Ptr>
     typename __gnu_cxx::__enable_if<
-      __is_random_access_iter<_II>::__value, bool>::__type
+      __is_any_random_access_iter<_II>::__value, bool>::__type
     __equal_aux1(_II __first1, _II __last1,
                _GLIBCXX_STD_C::_Deque_iterator<_Tp, _Ref, _Ptr> __first2)
     {
diff --git a/libstdc++-v3/include/bits/stl_algo.h 
b/libstdc++-v3/include/bits/stl_algo.h
index bbd1800af779..0d8f7af9d272 100644
--- a/libstdc++-v3/include/bits/stl_algo.h
+++ b/libstdc++-v3/include/bits/stl_algo.h
@@ -226,7 +226,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        return std::__find_if(__first, __last, __unary_pred);
 
       return std::__search_n_aux(__first, __last, __count, __unary_pred,
-                                std::__iterator_category(__first));
+                                std::__iter_concept_or_category(__first));
     }
 
   // find_end for forward iterators.
@@ -337,8 +337,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       __glibcxx_requires_valid_range(__first2, __last2);
 
       return std::__find_end(__first1, __last1, __first2, __last2,
-                            std::__iterator_category(__first1),
-                            std::__iterator_category(__first2),
+                            std::__iter_concept_or_category(__first1),
+                            std::__iter_concept_or_category(__first2),
                             __gnu_cxx::__ops::equal_to());
     }
 
@@ -388,8 +388,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       __glibcxx_requires_valid_range(__first2, __last2);
 
       return std::__find_end(__first1, __last1, __first2, __last2,
-                            std::__iterator_category(__first1),
-                            std::__iterator_category(__first2),
+                            std::__iter_concept_or_category(__first1),
+                            std::__iter_concept_or_category(__first2),
                             __comp);
     }
 
@@ -3495,9 +3495,9 @@ _GLIBCXX_END_INLINE_ABI_NAMESPACE(_V2)
                     _BinaryPredicate __pred)
     {
       using _Cat1
-       = typename iterator_traits<_ForwardIterator1>::iterator_category;
+       = __decltype(std::__iter_concept_or_category<_ForwardIterator1>());
       using _Cat2
-       = typename iterator_traits<_ForwardIterator2>::iterator_category;
+       = __decltype(std::__iter_concept_or_category<_ForwardIterator2>());
       using _It1_is_RA = is_same<_Cat1, random_access_iterator_tag>;
       using _It2_is_RA = is_same<_Cat2, random_access_iterator_tag>;
       constexpr bool __ra_iters = __and_<_It1_is_RA, _It2_is_RA>::value;
@@ -3800,7 +3800,7 @@ _GLIBCXX_BEGIN_NAMESPACE_ALGO
     for_each_n(_InputIterator __first, _Size __n, _Function __f)
     {
       auto __n2 = std::__size_to_integer(__n);
-      using _Cat = typename iterator_traits<_InputIterator>::iterator_category;
+      using _Cat = 
__decltype(std::__iter_concept_or_category<_InputIterator>());
       if constexpr (is_base_of_v<random_access_iterator_tag, _Cat>)
        {
          if (__n2 <= 0)
@@ -4450,7 +4450,7 @@ _GLIBCXX_BEGIN_NAMESPACE_ALGO
        return __result;
       return std::__unique_copy(__first, __last, __result,
                                __gnu_cxx::__ops::equal_to(),
-                               std::__iterator_category(__first));
+                               std::__iter_concept_or_category(__first));
     }
 
   /**
@@ -4491,7 +4491,7 @@ _GLIBCXX_BEGIN_NAMESPACE_ALGO
       if (__first == __last)
        return __result;
       return std::__unique_copy(__first, __last, __result, __binary_pred,
-                               std::__iterator_category(__first));
+                               std::__iter_concept_or_category(__first));
     }
 
 #if __cplusplus <= 201103L || _GLIBCXX_USE_DEPRECATED
@@ -5881,10 +5881,10 @@ _GLIBCXX_BEGIN_NAMESPACE_ALGO
           _SampleIterator __out, _Distance __n,
           _UniformRandomBitGenerator&& __g)
     {
-      using __pop_cat = typename
-       std::iterator_traits<_PopulationIterator>::iterator_category;
-      using __samp_cat = typename
-       std::iterator_traits<_SampleIterator>::iterator_category;
+      using __pop_cat
+       = __decltype(std::__iter_concept_or_category<_PopulationIterator>());
+      using __samp_cat
+       = typename iterator_traits<_SampleIterator>::iterator_category;
 
       static_assert(
          __or_<is_convertible<__pop_cat, forward_iterator_tag>,
diff --git a/libstdc++-v3/include/bits/stl_algobase.h 
b/libstdc++-v3/include/bits/stl_algobase.h
index 443cbef76dee..701910afd837 100644
--- a/libstdc++-v3/include/bits/stl_algobase.h
+++ b/libstdc++-v3/include/bits/stl_algobase.h
@@ -480,7 +480,7 @@ _GLIBCXX_END_NAMESPACE_CONTAINER
 
   template<bool _IsMove, typename _II, typename _Tp>
     typename __gnu_cxx::__enable_if<
-      __is_random_access_iter<_II>::__value,
+      __is_any_random_access_iter<_II>::__value,
       _GLIBCXX_STD_C::_Deque_iterator<_Tp, _Tp&, _Tp*> >::__type
     __copy_move_a1(_II, _II, _GLIBCXX_STD_C::_Deque_iterator<_Tp, _Tp&, _Tp*>);
 
@@ -769,7 +769,7 @@ _GLIBCXX_END_NAMESPACE_CONTAINER
 
   template<bool _IsMove, typename _II, typename _Tp>
     typename __gnu_cxx::__enable_if<
-      __is_random_access_iter<_II>::__value,
+      __is_any_random_access_iter<_II>::__value,
       _GLIBCXX_STD_C::_Deque_iterator<_Tp, _Tp&, _Tp*> >::__type
     __copy_move_backward_a1(_II, _II,
                            _GLIBCXX_STD_C::_Deque_iterator<_Tp, _Tp&, _Tp*>);
@@ -1219,7 +1219,7 @@ _GLIBCXX_END_NAMESPACE_CONTAINER
 
   template<typename _Tp, typename _Ref, typename _Ptr, typename _II>
     typename __gnu_cxx::__enable_if<
-      __is_random_access_iter<_II>::__value, bool>::__type
+      __is_any_random_access_iter<_II>::__value, bool>::__type
     __equal_aux1(_GLIBCXX_STD_C::_Deque_iterator<_Tp, _Ref, _Ptr>,
                 _GLIBCXX_STD_C::_Deque_iterator<_Tp, _Ref, _Ptr>,
                 _II);
@@ -1233,7 +1233,7 @@ _GLIBCXX_END_NAMESPACE_CONTAINER
 
   template<typename _II, typename _Tp, typename _Ref, typename _Ptr>
     typename __gnu_cxx::__enable_if<
-      __is_random_access_iter<_II>::__value, bool>::__type
+      __is_any_random_access_iter<_II>::__value, bool>::__type
     __equal_aux1(_II, _II,
                _GLIBCXX_STD_C::_Deque_iterator<_Tp, _Ref, _Ptr>);
 
@@ -1332,8 +1332,8 @@ _GLIBCXX_END_NAMESPACE_CONTAINER
                                   _II2 __first2, _II2 __last2,
                                   _Compare __comp)
     {
-      typedef typename iterator_traits<_II1>::iterator_category _Category1;
-      typedef typename iterator_traits<_II2>::iterator_category _Category2;
+      typedef __decltype(std::__iter_concept_or_category<_II1>()) _Category1;
+      typedef __decltype(std::__iter_concept_or_category<_II2>()) _Category2;
       typedef std::__lc_rai<_Category1, _Category2> __rai_type;
 
       __last1 = __rai_type::__newlast1(__first1, __last1, __first2, __last2);
@@ -1649,8 +1649,8 @@ _GLIBCXX_BEGIN_NAMESPACE_ALGO
     __equal4(_II1 __first1, _II1 __last1, _II2 __first2, _II2 __last2)
     {
       using _RATag = random_access_iterator_tag;
-      using _Cat1 = typename iterator_traits<_II1>::iterator_category;
-      using _Cat2 = typename iterator_traits<_II2>::iterator_category;
+      using _Cat1 = __decltype(std::__iter_concept_or_category<_II1>());
+      using _Cat2 = __decltype(std::__iter_concept_or_category<_II2>());
       using _RAIters = __and_<is_same<_Cat1, _RATag>, is_same<_Cat2, _RATag>>;
       if constexpr (_RAIters::value)
        {
@@ -1676,8 +1676,8 @@ _GLIBCXX_BEGIN_NAMESPACE_ALGO
             _BinaryPredicate __binary_pred)
     {
       using _RATag = random_access_iterator_tag;
-      using _Cat1 = typename iterator_traits<_II1>::iterator_category;
-      using _Cat2 = typename iterator_traits<_II2>::iterator_category;
+      using _Cat1 = __decltype(std::__iter_concept_or_category<_II1>());
+      using _Cat2 = __decltype(std::__iter_concept_or_category<_II2>());
       using _RAIters = __and_<is_same<_Cat1, _RATag>, is_same<_Cat2, _RATag>>;
       if constexpr (_RAIters::value)
        {
diff --git a/libstdc++-v3/include/bits/stl_iterator_base_funcs.h 
b/libstdc++-v3/include/bits/stl_iterator_base_funcs.h
index 7c80e1423e45..44816eb8e569 100644
--- a/libstdc++-v3/include/bits/stl_iterator_base_funcs.h
+++ b/libstdc++-v3/include/bits/stl_iterator_base_funcs.h
@@ -317,6 +317,68 @@ namespace __detail
 
 #endif // C++11
 
+#if __glibcxx_algorithm_iterator_requirements // C++ >= 20
+  template<typename _Iter>
+    consteval auto
+    __iter_concept_or_category()
+    {
+      if constexpr (__detail::__promotable_iterator<_Iter>)
+       {
+         using __type = __detail::__iter_traits<_Iter>::iterator_concept;
+         if constexpr (derived_from<__type, random_access_iterator_tag>)
+           return random_access_iterator_tag{};
+         else
+           return __type{};
+       }
+      else
+       return typename iterator_traits<_Iter>::iterator_category{};
+    }
+
+  template<typename _Iter>
+    __attribute__((__always_inline__))
+    constexpr auto
+    __iter_concept_or_category(const _Iter&)
+    { return std::__iter_concept_or_category<_Iter>(); }
+#else
+  template<typename _Iter>
+    __attribute__((__always_inline__))
+    inline _GLIBCXX_CONSTEXPR
+    typename iterator_traits<_Iter>::iterator_category
+    __iter_concept_or_category()
+    { return typename iterator_traits<_Iter>::iterator_category(); }
+
+  template<typename _Iter>
+    __attribute__((__always_inline__))
+    inline _GLIBCXX_CONSTEXPR
+    typename iterator_traits<_Iter>::iterator_category
+    __iter_concept_or_category(const _Iter&)
+    { return typename iterator_traits<_Iter>::iterator_category(); }
+#endif
+
+#if __cplusplus >= 201103L
+  // Like __is_random_access_iter, but based off of __iter_concept_or_category
+  // instead of iterator_traits::iterator_category.
+  template<typename _Iter,
+          typename _Cat = __decltype(__iter_concept_or_category<_Iter>())>
+    struct __is_any_random_access_iter
+      : is_base_of<random_access_iterator_tag, _Cat>
+    { enum { __value = __is_any_random_access_iter::value }; };
+#else
+  template<typename _Iter,
+          typename _Cat = __decltype(__iter_concept_or_category<_Iter>())>
+    struct __is_any_random_access_iter
+    { enum { __value = __is_base_of(random_access_iterator_tag, _Cat) }; };
+#endif
+
+#if __cplusplus >= 202002L
+  // A wrapper around ranges::iter_move that also converts to the iterator's
+  // value type.
+#define _GLIBCXX_ITER_MOVE(__it) \
+  std::iter_value_t<decltype(__it)>(std::ranges::iter_move(__it))
+#else
+#define _GLIBCXX_ITER_MOVE(__it) _GLIBCXX_MOVE(*__it)
+#endif
+
 _GLIBCXX_END_NAMESPACE_VERSION
 } // namespace
 
diff --git a/libstdc++-v3/include/bits/stl_uninitialized.h 
b/libstdc++-v3/include/bits/stl_uninitialized.h
index 70a564659814..96f740e86e7b 100644
--- a/libstdc++-v3/include/bits/stl_uninitialized.h
+++ b/libstdc++-v3/include/bits/stl_uninitialized.h
@@ -1177,7 +1177,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     uninitialized_copy_n(_InputIterator __first, _Size __n,
                         _ForwardIterator __result)
     { return std::__uninitialized_copy_n(__first, __n, __result,
-                                        std::__iterator_category(__first)); }
+                                        
std::__iter_concept_or_category(__first)); }
 
   /// @cond undocumented
   template<typename _InputIterator, typename _Size, typename _ForwardIterator>
@@ -1188,7 +1188,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     {
       return
        std::__uninitialized_copy_n_pair(__first, __n, __result,
-                                        std::__iterator_category(__first));
+                                        
std::__iter_concept_or_category(__first));
     }
   /// @endcond
 #endif
diff --git a/libstdc++-v3/include/bits/version.def 
b/libstdc++-v3/include/bits/version.def
index 29ecf15c7e39..9177af610b16 100644
--- a/libstdc++-v3/include/bits/version.def
+++ b/libstdc++-v3/include/bits/version.def
@@ -1972,6 +1972,15 @@ ftms = {
   };
 };
 
+ftms = {
+  name = algorithm_iterator_requirements;
+  values = {
+    v = 202207;
+    // P2408R5 is a C++23 feature, but we support it in C++20.
+    cxxmin = 20;
+  };
+};
+
 ftms = {
   name = algorithm_default_value_type;
   values = {
diff --git a/libstdc++-v3/include/bits/version.h 
b/libstdc++-v3/include/bits/version.h
index 5901d27113d7..3a930db2239b 100644
--- a/libstdc++-v3/include/bits/version.h
+++ b/libstdc++-v3/include/bits/version.h
@@ -2206,6 +2206,16 @@
 #endif /* !defined(__cpp_lib_observable_checkpoint) */
 #undef __glibcxx_want_observable_checkpoint
 
+#if !defined(__cpp_lib_algorithm_iterator_requirements)
+# if (__cplusplus >= 202002L)
+#  define __glibcxx_algorithm_iterator_requirements 202207L
+#  if defined(__glibcxx_want_all) || 
defined(__glibcxx_want_algorithm_iterator_requirements)
+#   define __cpp_lib_algorithm_iterator_requirements 202207L
+#  endif
+# endif
+#endif /* !defined(__cpp_lib_algorithm_iterator_requirements) */
+#undef __glibcxx_want_algorithm_iterator_requirements
+
 #if !defined(__cpp_lib_algorithm_default_value_type)
 # if (__cplusplus >  202302L)
 #  define __glibcxx_algorithm_default_value_type 202403L
diff --git a/libstdc++-v3/include/std/algorithm 
b/libstdc++-v3/include/std/algorithm
index 1563cdf2b17c..a39474df8b1d 100644
--- a/libstdc++-v3/include/std/algorithm
+++ b/libstdc++-v3/include/std/algorithm
@@ -66,6 +66,7 @@
 #endif
 
 #define __glibcxx_want_algorithm_default_value_type
+#define __glibcxx_want_algorithm_iterator_requirements
 #define __glibcxx_want_clamp
 #define __glibcxx_want_constexpr_algorithms
 #define __glibcxx_want_freestanding_algorithm
diff --git a/libstdc++-v3/include/std/memory b/libstdc++-v3/include/std/memory
index 9763760b8d61..52056d2f02a5 100644
--- a/libstdc++-v3/include/std/memory
+++ b/libstdc++-v3/include/std/memory
@@ -102,6 +102,7 @@
 #endif
 
 #define __glibcxx_want_addressof_constexpr
+#define __glibcxx_want_algorithm_iterator_requirements
 #define __glibcxx_want_allocator_traits_is_always_equal
 #define __glibcxx_want_assume_aligned
 #define __glibcxx_want_atomic_shared_ptr
diff --git a/libstdc++-v3/include/std/numeric b/libstdc++-v3/include/std/numeric
index cbabf031216e..830a458bfd26 100644
--- a/libstdc++-v3/include/std/numeric
+++ b/libstdc++-v3/include/std/numeric
@@ -81,6 +81,7 @@
 # include <limits>
 #endif
 
+#define __glibcxx_want_algorithm_iterator_requirements
 #define __glibcxx_want_constexpr_numeric
 #define __glibcxx_want_gcd
 #define __glibcxx_want_gcd_lcm
@@ -298,7 +299,7 @@ namespace __detail
       static_assert(is_invocable_r_v<_Tp, _BinaryOperation&, __ref, _Tp&>);
       static_assert(is_invocable_r_v<_Tp, _BinaryOperation&, _Tp&, _Tp&>);
       static_assert(is_invocable_r_v<_Tp, _BinaryOperation&, __ref, __ref>);
-      if constexpr (__is_random_access_iter<_InputIterator>::value)
+      if constexpr (__is_any_random_access_iter<_InputIterator>::value)
        {
          while ((__last - __first) >= 4)
            {
@@ -378,8 +379,8 @@ namespace __detail
                     _BinaryOperation1 __binary_op1,
                     _BinaryOperation2 __binary_op2)
     {
-      if constexpr (__and_v<__is_random_access_iter<_InputIterator1>,
-                           __is_random_access_iter<_InputIterator2>>)
+      if constexpr (__and_v<__is_any_random_access_iter<_InputIterator1>,
+                           __is_any_random_access_iter<_InputIterator2>>)
        {
          while ((__last1 - __first1) >= 4)
            {
@@ -445,7 +446,7 @@ namespace __detail
     transform_reduce(_InputIterator __first, _InputIterator __last, _Tp __init,
                     _BinaryOperation __binary_op, _UnaryOperation __unary_op)
     {
-      if constexpr (__is_random_access_iter<_InputIterator>::value)
+      if constexpr (__is_any_random_access_iter<_InputIterator>::value)
        {
          while ((__last - __first) >= 4)
            {
@@ -582,7 +583,7 @@ namespace __detail
     {
       if (__first != __last)
        {
-         auto __init = std::move(*__first);
+         auto __init = _GLIBCXX_ITER_MOVE(__first);
          *__result++ = __init;
          ++__first;
          if (__first != __last)
diff --git a/libstdc++-v3/testsuite/25_algorithms/find_end/c++20_iter.cc 
b/libstdc++-v3/testsuite/25_algorithms/find_end/c++20_iter.cc
new file mode 100644
index 000000000000..6bd1cead1c36
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/find_end/c++20_iter.cc
@@ -0,0 +1,23 @@
+// Verify std::find_end is C++20 iterator aware as per P2408R5.
+// { dg-do compile { target c++20 } }
+
+#include <algorithm>
+#include <ranges>
+#include <testsuite_hooks.h>
+
+constexpr bool
+test01()
+{
+  auto r = std::views::iota(0, 10);
+  auto s = std::views::iota(5, 10);
+  auto it = r.begin();
+  auto jt = s.begin();
+  static_assert( std::random_access_iterator<decltype(it)>);
+  static_assert( 
std::same_as<std::iterator_traits<decltype(it)>::iterator_category,
+                             std::input_iterator_tag> );
+  it = std::find_end(it, it+10, jt, jt+5);
+  VERIFY( it == r.begin() + 5 );
+  return true;
+}
+
+static_assert(test01());
diff --git a/libstdc++-v3/testsuite/25_algorithms/sample/c++20_iter.cc 
b/libstdc++-v3/testsuite/25_algorithms/sample/c++20_iter.cc
new file mode 100644
index 000000000000..3933b3a2a2c2
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/sample/c++20_iter.cc
@@ -0,0 +1,23 @@
+// Verify std::sample is C++20 iterator aware as per P2408R5.
+// { dg-do compile { target c++20 } }
+
+#include <algorithm>
+#include <random>
+#include <ranges>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+std::mt19937 rng;
+
+void
+test01()
+{
+  auto r = std::views::iota(10);
+  auto it = r.begin();
+  static_assert( std::random_access_iterator<decltype(it)>);
+  static_assert( 
std::same_as<std::iterator_traits<decltype(it)>::iterator_category,
+                             std::input_iterator_tag> );
+  int buf[10];
+  __gnu_test::output_container<int> s(buf);
+  std::sample(it, it+10, s.begin(), 10, rng);
+}
diff --git a/libstdc++-v3/testsuite/25_algorithms/search_n/c++20_iter.cc 
b/libstdc++-v3/testsuite/25_algorithms/search_n/c++20_iter.cc
new file mode 100644
index 000000000000..9787358e6674
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/search_n/c++20_iter.cc
@@ -0,0 +1,21 @@
+// Verify std::search_n is Ranges iterator aware as per P2408R5.
+// { dg-do compile { target c++20 } }
+
+#include <algorithm>
+#include <ranges>
+#include <testsuite_hooks.h>
+
+constexpr bool
+test01()
+{
+  auto r = std::views::iota(0, 10);
+  auto it = r.begin();
+  static_assert( std::random_access_iterator<decltype(it)>);
+  static_assert( 
std::same_as<std::iterator_traits<decltype(it)>::iterator_category,
+                             std::input_iterator_tag> );
+  it = std::search_n(it, it+10, 1, 5);
+  VERIFY( it == r.begin() + 5 );
+  return true;
+}
+
+static_assert(test01());
diff --git a/libstdc++-v3/testsuite/25_algorithms/unique_copy/c++20_iter.cc 
b/libstdc++-v3/testsuite/25_algorithms/unique_copy/c++20_iter.cc
new file mode 100644
index 000000000000..20017ced8672
--- /dev/null
+++ b/libstdc++-v3/testsuite/25_algorithms/unique_copy/c++20_iter.cc
@@ -0,0 +1,33 @@
+// Verify std::unique_copy is C++20 iterator aware as per P2408R5.
+// { dg-do compile { target c++20 } }
+
+#include <algorithm>
+#include <ranges>
+#include <testsuite_hooks.h>
+#include <testsuite_iterators.h>
+
+struct noncopyable
+{
+  constexpr operator int() { return 42; }
+  noncopyable() = default;
+  noncopyable(const noncopyable&) = delete;
+  noncopyable& operator=(const noncopyable&) = delete;
+  friend auto operator<=>(const noncopyable&, const noncopyable&) = default;
+};
+
+constexpr bool
+test01()
+{
+  auto r = std::views::iota(10)
+    | std::views::transform([](int) { return noncopyable{}; });
+  auto it = r.begin();
+  static_assert( std::random_access_iterator<decltype(it)>);
+  static_assert( 
std::same_as<std::iterator_traits<decltype(it)>::iterator_category,
+                             std::input_iterator_tag> );
+  int buf[10];
+  __gnu_test::input_container<int> s(buf);
+  auto jt = std::unique_copy(it, it+10, s.begin());
+  return true;
+}
+
+static_assert(test01());
diff --git a/libstdc++-v3/testsuite/26_numerics/inclusive_scan/c++20_iter.cc 
b/libstdc++-v3/testsuite/26_numerics/inclusive_scan/c++20_iter.cc
new file mode 100644
index 000000000000..d8f2781fc669
--- /dev/null
+++ b/libstdc++-v3/testsuite/26_numerics/inclusive_scan/c++20_iter.cc
@@ -0,0 +1,26 @@
+// Verify std::inclusive_scan is C++20 iterator aware as per P2408R5.
+// { dg-do compile { target c++23 } }
+
+#include <numeric>
+#include <ranges>
+#include <testsuite_hooks.h>
+
+constexpr bool
+test01()
+{
+  int x[10] = {1,2,3,4,5,6,7,8,9,10};
+  auto r = std::views::zip(x);
+  auto it = r.begin();
+  static_assert( std::random_access_iterator<decltype(it)>);
+  static_assert( 
std::same_as<std::iterator_traits<decltype(it)>::iterator_category,
+                             std::input_iterator_tag> );
+  std::tuple<int> y[10];
+  std::inclusive_scan(it, it+10, y,
+                     [](std::tuple<int> a, std::tuple<int> b) -> 
std::tuple<int> {
+                       return std::get<0>(a) + std::get<0>(b);
+                     });
+  VERIFY( std::get<0>(y[9]) == 55 );
+  return true;
+}
+
+static_assert(test01());
-- 
2.52.0.84.g6ab38b7e9c

Reply via email to