[libcxx-commits] [libcxx] [libc++] Refactor for_each_n (PR #150635)

Nikolas Klauser via libcxx-commits libcxx-commits at lists.llvm.org
Fri Jul 25 08:40:55 PDT 2025


https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/150635

This does multiple things:
- `if constexpr` is used not to simplify overload resolution
- the segmented iterator optimization is always applied, not just for random access local iterators



>From 04cb7c35d803c39acc1dbdc9fc62d32a028dcbe6 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Fri, 25 Jul 2025 17:37:55 +0200
Subject: [PATCH] [libc++] Refactor for_each_n

---
 libcxx/include/__algorithm/for_each_n.h       | 91 +++++++------------
 .../include/__algorithm/for_each_n_segment.h  | 24 ++---
 .../include/__algorithm/ranges_for_each_n.h   |  5 +-
 .../include/__iterator/unreachable_sentinel.h | 33 +++++++
 4 files changed, 76 insertions(+), 77 deletions(-)

diff --git a/libcxx/include/__algorithm/for_each_n.h b/libcxx/include/__algorithm/for_each_n.h
index 04650e15b6362..24664c4517300 100644
--- a/libcxx/include/__algorithm/for_each_n.h
+++ b/libcxx/include/__algorithm/for_each_n.h
@@ -12,15 +12,16 @@
 
 #include <__algorithm/for_each.h>
 #include <__algorithm/for_each_n_segment.h>
+#include <__algorithm/min.h>
 #include <__config>
+#include <__cstddef/size_t.h>
 #include <__functional/identity.h>
 #include <__iterator/iterator_traits.h>
 #include <__iterator/segmented_iterator.h>
-#include <__type_traits/disjunction.h>
-#include <__type_traits/enable_if.h>
+#include <__iterator/unreachable_sentinel.h>
 #include <__type_traits/invoke.h>
-#include <__type_traits/negation.h>
-#include <__utility/convert_to_integral.h>
+#include <__type_traits/is_same.h>
+#include <__utility/exchange.h>
 #include <__utility/move.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -30,73 +31,47 @@
 _LIBCPP_PUSH_MACROS
 #include <__undef_macros>
 
+#if _LIBCPP_STD_VER >= 17
+
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-template <class _InputIterator,
-          class _Size,
-          class _Func,
-          class _Proj,
-          __enable_if_t<!__has_random_access_iterator_category<_InputIterator>::value &&
-                            _Or<integral_constant<bool, !__is_segmented_iterator_v<_InputIterator> >,
-                                _Not<__has_random_access_local_iterator<_InputIterator> > >::value,
-                        int> = 0>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _InputIterator
-__for_each_n(_InputIterator __first, _Size __orig_n, _Func& __f, _Proj& __proj) {
-  typedef decltype(std::__convert_to_integral(__orig_n)) _IntegralSize;
-  _IntegralSize __n = __orig_n;
-  while (__n > 0) {
-    std::__invoke(__f, std::__invoke(__proj, *__first));
-    ++__first;
-    --__n;
+template <class _Iter, class _Sent, class _Size, class _Func, class _Proj>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Iter
+__for_each_n(_Iter __first, _Sent __last, _Size& __n, _Func& __func, _Proj& __proj) {
+  if constexpr (__has_random_access_iterator_category<_Iter>::value) {
+    if constexpr (is_same_v<_Sent, __unreachable_sentinel_t>) {
+      return std::__for_each(__first, __first + std::exchange(__n, 0), __func, __proj);
+    } else {
+      auto __count = std::min<size_t>(__n, __last - __first);
+      __n -= __count;
+      return std::__for_each(__first, __first + __count, __func, __proj);
+    }
+  } else if constexpr (__is_segmented_iterator_v<_Iter>) {
+    return std::__for_each_n_segment(__first, __n, [&](auto __lfirst, auto __llast, _Size __max) {
+      std::__for_each_n(__lfirst, __llast, __max, __func, __proj);
+      return __max;
+    });
+  } else {
+    while (__n > 0 && __first != __last) {
+      std::__invoke(__func, std::__invoke(__proj, *__first));
+      ++__first;
+      --__n;
+    }
+    return std::move(__first);
   }
-  return std::move(__first);
-}
-
-template <class _RandIter,
-          class _Size,
-          class _Func,
-          class _Proj,
-          __enable_if_t<__has_random_access_iterator_category<_RandIter>::value, int> = 0>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _RandIter
-__for_each_n(_RandIter __first, _Size __orig_n, _Func& __f, _Proj& __proj) {
-  typename std::iterator_traits<_RandIter>::difference_type __n = __orig_n;
-  auto __last                                                   = __first + __n;
-  std::__for_each(__first, __last, __f, __proj);
-  return __last;
-}
-
-#ifndef _LIBCPP_CXX03_LANG
-template <class _SegmentedIterator,
-          class _Size,
-          class _Func,
-          class _Proj,
-          __enable_if_t<!__has_random_access_iterator_category<_SegmentedIterator>::value &&
-                            __is_segmented_iterator_v<_SegmentedIterator> &&
-                            __has_random_access_iterator_category<
-                                typename __segmented_iterator_traits<_SegmentedIterator>::__local_iterator>::value,
-                        int> = 0>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _SegmentedIterator
-__for_each_n(_SegmentedIterator __first, _Size __orig_n, _Func& __f, _Proj& __proj) {
-  using __local_iterator_t = typename __segmented_iterator_traits<_SegmentedIterator>::__local_iterator;
-  return std::__for_each_n_segment(__first, __orig_n, [&](__local_iterator_t __lfirst, __local_iterator_t __llast) {
-    std::__for_each(__lfirst, __llast, __f, __proj);
-  });
 }
-#endif // !_LIBCPP_CXX03_LANG
-
-#if _LIBCPP_STD_VER >= 17
 
 template <class _InputIterator, class _Size, class _Func>
 inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _InputIterator
 for_each_n(_InputIterator __first, _Size __orig_n, _Func __f) {
   __identity __proj;
-  return std::__for_each_n(__first, __orig_n, __f, __proj);
+  return std::__for_each_n(__first, __unreachable_sentinel, __orig_n, __f, __proj);
 }
 
-#endif // _LIBCPP_STD_VER >= 17
-
 _LIBCPP_END_NAMESPACE_STD
 
+#endif // _LIBCPP_STD_VER >= 17
+
 _LIBCPP_POP_MACROS
 
 #endif // _LIBCPP___ALGORITHM_FOR_EACH_N_H
diff --git a/libcxx/include/__algorithm/for_each_n_segment.h b/libcxx/include/__algorithm/for_each_n_segment.h
index a433df5d098ae..2e5c8ba2d336a 100644
--- a/libcxx/include/__algorithm/for_each_n_segment.h
+++ b/libcxx/include/__algorithm/for_each_n_segment.h
@@ -26,36 +26,28 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <class _SegmentedIterator, class _Size, class _Functor>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _SegmentedIterator
-__for_each_n_segment(_SegmentedIterator __first, _Size __orig_n, _Functor __func) {
+__for_each_n_segment(_SegmentedIterator __first, _Size __n, _Functor __func) {
   static_assert(__is_segmented_iterator_v<_SegmentedIterator> &&
                     __has_random_access_iterator_category<
                         typename __segmented_iterator_traits<_SegmentedIterator>::__local_iterator>::value,
                 "__for_each_n_segment only works with segmented iterators with random-access local iterators");
-  if (__orig_n <= 0)
+  if (__n <= 0)
     return __first;
 
   using _Traits        = __segmented_iterator_traits<_SegmentedIterator>;
   using __local_iter_t = typename _Traits::__local_iterator;
-  using __difference_t = typename std::iterator_traits<__local_iter_t>::difference_type;
-  __difference_t __n   = __orig_n;
-  auto __seg           = _Traits::__segment(__first);
+  auto __segment_iter  = _Traits::__segment(__first);
   auto __local_first   = _Traits::__local(__first);
   __local_iter_t __local_last;
 
+  __n = __func(_Traits::__local(__first), _Traits::__end(__segment_iter), __n);
+
   while (__n > 0) {
-    __local_last    = _Traits::__end(__seg);
-    auto __seg_size = __local_last - __local_first;
-    if (__n <= __seg_size) {
-      __local_last = __local_first + __n;
-      __func(__local_first, __local_last);
-      break;
-    }
-    __func(__local_first, __local_last);
-    __n -= __seg_size;
-    __local_first = _Traits::__begin(++__seg);
+    ++__segment_iter;
+    __n = __func(_Traits::__begin(__segment_iter), _Traits::__end(__segment_iter), __n);
   }
 
-  return _Traits::__compose(__seg, __local_last);
+  return _Traits::__compose(__segment_iter, __local_last);
 }
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__algorithm/ranges_for_each_n.h b/libcxx/include/__algorithm/ranges_for_each_n.h
index 3aab1b79c10a1..7beba8f2d3693 100644
--- a/libcxx/include/__algorithm/ranges_for_each_n.h
+++ b/libcxx/include/__algorithm/ranges_for_each_n.h
@@ -15,9 +15,8 @@
 #include <__functional/identity.h>
 #include <__iterator/concepts.h>
 #include <__iterator/incrementable_traits.h>
-#include <__iterator/iterator_traits.h>
 #include <__iterator/projected.h>
-#include <__ranges/concepts.h>
+#include <__iterator/unreachable_sentinel.h>
 #include <__utility/move.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -40,7 +39,7 @@ struct __for_each_n {
   template <input_iterator _Iter, class _Proj = identity, indirectly_unary_invocable<projected<_Iter, _Proj>> _Func>
   _LIBCPP_HIDE_FROM_ABI constexpr for_each_n_result<_Iter, _Func>
   operator()(_Iter __first, iter_difference_t<_Iter> __count, _Func __func, _Proj __proj = {}) const {
-    auto __last = std::__for_each_n(std::move(__first), __count, __func, __proj);
+    auto __last = std::__for_each_n(std::move(__first), __unreachable_sentinel, __count, __func, __proj);
     return {std::move(__last), std::move(__func)};
   }
 };
diff --git a/libcxx/include/__iterator/unreachable_sentinel.h b/libcxx/include/__iterator/unreachable_sentinel.h
index 77e663da4b3a6..0351b88d4b234 100644
--- a/libcxx/include/__iterator/unreachable_sentinel.h
+++ b/libcxx/include/__iterator/unreachable_sentinel.h
@@ -12,6 +12,9 @@
 
 #include <__config>
 #include <__iterator/concepts.h>
+#include <__type_traits/enable_if.h>
+#include <__type_traits/is_same.h>
+#include <__type_traits/remove_cvref.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -19,6 +22,36 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
+inline constexpr struct __unreachable_sentinel_t {} __unreachable_sentinel;
+
+template <class _UnreachableSentinel,
+          class _Iter,
+          __enable_if_t<is_same<__remove_cvref_t<_UnreachableSentinel>, __unreachable_sentinel_t>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(_UnreachableSentinel&&, _Iter&&) {
+  return false;
+}
+
+template <class _UnreachableSentinel,
+          class _Iter,
+          __enable_if_t<is_same<__remove_cvref_t<_UnreachableSentinel>, __unreachable_sentinel_t>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(_Iter&&, _UnreachableSentinel&&) {
+  return false;
+}
+
+template <class _UnreachableSentinel,
+          class _Iter,
+          __enable_if_t<is_same<__remove_cvref_t<_UnreachableSentinel>, __unreachable_sentinel_t>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(_UnreachableSentinel&&, _Iter&&) {
+  return true;
+}
+
+template <class _UnreachableSentinel,
+          class _Iter,
+          __enable_if_t<is_same<__remove_cvref_t<_UnreachableSentinel>, __unreachable_sentinel_t>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(_Iter&&, _UnreachableSentinel&&) {
+  return true;
+}
+
 #if _LIBCPP_STD_VER >= 20
 
 struct unreachable_sentinel_t {



More information about the libcxx-commits mailing list