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)
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)
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)
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)
{
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.
__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());
}
__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);
}
_ForwardIterator2 __first2, _ForwardIterator2 __last2,
_BinaryPredicate __pred)
{
- using _Cat1
- = typename iterator_traits<_ForwardIterator1>::iterator_category;
- using _Cat2
- = typename iterator_traits<_ForwardIterator2>::iterator_category;
+ using _Cat1 = decltype(std::__iter_concept_or_category<_ForwardIterator1>());
+ using _Cat2 = 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;
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)
return __result;
return std::__unique_copy(__first, __last, __result,
__gnu_cxx::__ops::equal_to(),
- std::__iterator_category(__first));
+ std::__iter_concept_or_category(__first));
}
/**
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
_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>,
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*>);
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*>);
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);
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>);
_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);
__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)
{
_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)
{
#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
+
+ // 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
+#if __cplusplus >= 201103L
+ : is_base_of<random_access_iterator_tag, _Cat>
+#endif
+ { enum { __value = __is_base_of(random_access_iterator_tag, _Cat) }; };
+
+// A wrapper around ranges::iter_move that also converts to the iterator's
+// value type.
+#if __cplusplus >= 202002L
+#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
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>
{
return
std::__uninitialized_copy_n_pair(__first, __n, __result,
- std::__iterator_category(__first));
+ std::__iter_concept_or_category(__first));
}
/// @endcond
#endif
};
};
+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 = {
#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
#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
#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
#endif
#include <bits/c++config.h>
-#include <bits/stl_iterator_base_types.h>
+#include <bits/stl_iterator_base_funcs.h>
#include <bits/stl_numeric.h>
#ifdef _GLIBCXX_PARALLEL
# include <limits>
#endif
+#define __glibcxx_want_algorithm_iterator_requirements
#define __glibcxx_want_constexpr_numeric
#define __glibcxx_want_gcd
#define __glibcxx_want_gcd_lcm
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)
{
_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)
{
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)
{
{
if (__first != __last)
{
- auto __init = std::move(*__first);
+ auto __init = _GLIBCXX_ITER_MOVE(__first);
*__result++ = __init;
++__first;
if (__first != __last)
--- /dev/null
+// 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());
--- /dev/null
+// 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);
+}
--- /dev/null
+// Verify std::search_n 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 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());
--- /dev/null
+// 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());
--- /dev/null
+// 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());