[libcxx-commits] [libcxx] [libc++] Optimize std::count and std::count_if for segmented iterators (PR #198375)
Aryan Naraghi via libcxx-commits
libcxx-commits at lists.llvm.org
Sat May 23 10:21:45 PDT 2026
https://github.com/aryann updated https://github.com/llvm/llvm-project/pull/198375
>From bec55ed9b32b140a94e9763de3fab590dc3c830d Mon Sep 17 00:00:00 2001
From: Aryan Naraghi <aryan.naraghi at gmail.com>
Date: Mon, 18 May 2026 11:15:42 -0700
Subject: [PATCH 1/7] Implements segmented iterators for std::count and
std::count_if
---
libcxx/include/__algorithm/count.h | 21 +++++++++++++++++++++
libcxx/include/__algorithm/count_if.h | 20 ++++++++++++++++++++
2 files changed, 41 insertions(+)
diff --git a/libcxx/include/__algorithm/count.h b/libcxx/include/__algorithm/count.h
index 8529d110a30aa..f1b230691584e 100644
--- a/libcxx/include/__algorithm/count.h
+++ b/libcxx/include/__algorithm/count.h
@@ -10,6 +10,7 @@
#ifndef _LIBCPP___ALGORITHM_COUNT_H
#define _LIBCPP___ALGORITHM_COUNT_H
+#include <__algorithm/for_each_segment.h>
#include <__algorithm/iterator_operations.h>
#include <__algorithm/min.h>
#include <__bit/invert_if.h>
@@ -18,6 +19,7 @@
#include <__functional/identity.h>
#include <__fwd/bit_reference.h>
#include <__iterator/iterator_traits.h>
+#include <__iterator/segmented_iterator.h>
#include <__type_traits/enable_if.h>
#include <__type_traits/invoke.h>
@@ -41,6 +43,25 @@ __count(_Iter __first, _Sent __last, const _Tp& __value, _Proj& __proj) {
return __r;
}
+// segmented iterator implementation
+#ifndef _LIBCPP_CXX03_LANG
+template <class _AlgPolicy,
+ class _SegmentedIterator,
+ class _Tp,
+ class _Proj,
+ __enable_if_t<__is_segmented_iterator_v<_SegmentedIterator>, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20
+typename _IterOps<_AlgPolicy>::template __difference_type<_SegmentedIterator>
+__count(_SegmentedIterator __first, _SegmentedIterator __last, const _Tp& __value, _Proj& __proj) {
+ typename _IterOps<_AlgPolicy>::template __difference_type<_SegmentedIterator> __r(0);
+ using __local_iterator_t = typename __segmented_iterator_traits<_SegmentedIterator>::__local_iterator;
+ std::__for_each_segment(__first, __last, [&](__local_iterator_t __lfirst, __local_iterator_t __llast) {
+ __r += std::__count<_AlgPolicy>(__lfirst, __llast, __value, __proj);
+ });
+ return __r;
+}
+#endif // _LIBCPP_CXX03_LANG
+
// __bit_iterator implementation
template <bool _ToCount, class _Cp, bool _IsConst>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 typename __bit_iterator<_Cp, _IsConst>::difference_type
diff --git a/libcxx/include/__algorithm/count_if.h b/libcxx/include/__algorithm/count_if.h
index 26f945e6bd98c..28329b948b687 100644
--- a/libcxx/include/__algorithm/count_if.h
+++ b/libcxx/include/__algorithm/count_if.h
@@ -10,10 +10,12 @@
#ifndef _LIBCPP___ALGORITHM_COUNT_IF_H
#define _LIBCPP___ALGORITHM_COUNT_IF_H
+#include <__algorithm/for_each_segment.h>
#include <__algorithm/iterator_operations.h>
#include <__config>
#include <__functional/identity.h>
#include <__iterator/iterator_traits.h>
+#include <__iterator/segmented_iterator.h>
#include <__type_traits/invoke.h>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -33,6 +35,24 @@ __count_if(_Iter __first, _Sent __last, _Pred& __pred, _Proj& __proj) {
return __counter;
}
+// segmented iterator implementation
+#ifndef _LIBCPP_CXX03_LANG
+template <class _AlgPolicy,
+ class _SegmentedIterator,
+ class _Proj,
+ class _Pred,
+ __enable_if_t<__is_segmented_iterator_v<_SegmentedIterator>, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 __policy_iter_diff_t<_AlgPolicy, _SegmentedIterator>
+__count_if(_SegmentedIterator __first, _SegmentedIterator __last, _Pred& __pred, _Proj& __proj) {
+ __policy_iter_diff_t<_AlgPolicy, _SegmentedIterator> __counter(0);
+ using __local_iterator_t = typename __segmented_iterator_traits<_SegmentedIterator>::__local_iterator;
+ std::__for_each_segment(__first, __last, [&](__local_iterator_t __lfirst, __local_iterator_t __llast) {
+ __counter += std::__count_if<_AlgPolicy>(__lfirst, __llast, __pred, __proj);
+ });
+ return __counter;
+}
+#endif // _LIBCPP_CXX03_LANG
+
template <class _InputIterator, class _Predicate>
[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20
typename iterator_traits<_InputIterator>::difference_type
>From 70d9d6cdee59e15a5cdac4ba75d04a6ce0c7d882 Mon Sep 17 00:00:00 2001
From: Aryan Naraghi <aryan.naraghi at gmail.com>
Date: Mon, 18 May 2026 11:19:02 -0700
Subject: [PATCH 2/7] Add release notes
---
libcxx/docs/ReleaseNotes/23.rst | 2 ++
1 file changed, 2 insertions(+)
diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index a55869a8bf783..d7b396c0f6e6c 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -57,6 +57,8 @@ Improvements and New Features
for ``std::deque<int>`` iterators.
- ``std::copy(CharT*, CharT*, ostreambuf_iterator<CharT>)`` has been optimized, resulting in performance improvements
of up to 25x.
+- The ``std::count`` and ``std::count_if`` algorithms have been optimized for
+ segmented iterators.
Deprecations and Removals
-------------------------
>From a229fe5f0786ee514d056b050c7b08e77f357c5d Mon Sep 17 00:00:00 2001
From: Aryan Naraghi <aryan.naraghi at gmail.com>
Date: Mon, 18 May 2026 11:44:08 -0700
Subject: [PATCH 3/7] Adds unit tests for std::count
---
.../alg.nonmodifying/alg.count/count.pass.cpp | 39 +++++++++++++++++++
1 file changed, 39 insertions(+)
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.count/count.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.count/count.pass.cpp
index 1561dcf8a6352..c331dd8ef44b7 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.count/count.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.count/count.pass.cpp
@@ -20,12 +20,14 @@
#include <cassert>
#include <cstddef>
#include <cstdint>
+#include <deque>
#include <vector>
#include "sized_allocator.h"
#include "test_macros.h"
#include "test_iterators.h"
#include "type_algorithms.h"
+#include <list>
struct Test {
template <class Iter>
@@ -87,11 +89,48 @@ TEST_CONSTEXPR_CXX20 bool test() {
return true;
}
+void test_deque_and_join_view_iterators() {
+ {
+ // Verify that segmented deque iterators work properly
+ const int sizes[] = {0, 1, 2, 1023, 1024, 1025, 2047, 2048, 2049};
+ for (const int size : sizes) {
+ std::deque<int> deque(size, 1);
+
+ std::ptrdiff_t twos = 0;
+ for (int i = 0; i < size; i += 3) {
+ deque[i] = 2;
+ ++twos;
+ }
+ std::ptrdiff_t ones = deque.size() - twos;
+
+ assert(std::count(deque.begin(), deque.end(), 1) == ones);
+ assert(std::count(deque.begin(), deque.end(), 2) == twos);
+ assert(std::count(deque.begin(), deque.end(), 99) == 0);
+ }
+ }
+
+#if TEST_STD_VER >= 20
+ {
+ // Verify that join_view of lists work properly
+ std::list<std::list<int>> list = {{}, {0}, {1, 2}, {}, {0, 1, 2}, {0, 1, 2, 0}, {1}, {2, 0, 1}};
+ auto joined = list | std::views::join;
+
+ assert(std::count(joined.begin(), joined.end(), 0) == 5);
+ assert(std::count(joined.begin(), joined.end(), 1) == 5);
+ assert(std::count(joined.begin(), joined.end(), 2) == 4);
+ assert(std::count(joined.begin(), joined.end(), 99) == 0);
+ }
+#endif // TEST_STD_VER >= 20
+}
+
int main(int, char**) {
test();
#if TEST_STD_VER >= 20
static_assert(test());
#endif
+#if TEST_STD_VER >= 11
+ test_deque_and_join_view_iterators();
+#endif
return 0;
}
>From 3cce3998ce746c76d6745c2ab52d4243826157a1 Mon Sep 17 00:00:00 2001
From: Aryan Naraghi <aryan.naraghi at gmail.com>
Date: Mon, 18 May 2026 11:46:44 -0700
Subject: [PATCH 4/7] Adds more test cases to std::count
---
.../alg.nonmodifying/alg.count/count.pass.cpp | 21 ++++++++++++++-----
1 file changed, 16 insertions(+), 5 deletions(-)
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.count/count.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.count/count.pass.cpp
index c331dd8ef44b7..ce88f14425ab5 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.count/count.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.count/count.pass.cpp
@@ -89,19 +89,19 @@ TEST_CONSTEXPR_CXX20 bool test() {
return true;
}
-void test_deque_and_join_view_iterators() {
+void test_segmented_iterators() {
{
// Verify that segmented deque iterators work properly
- const int sizes[] = {0, 1, 2, 1023, 1024, 1025, 2047, 2048, 2049};
+ const int sizes[] = {0, 1, 2, 1023, 1024, 1025, 2047, 2048, 2049, 4097};
for (const int size : sizes) {
std::deque<int> deque(size, 1);
- std::ptrdiff_t twos = 0;
+ int twos = 0;
for (int i = 0; i < size; i += 3) {
deque[i] = 2;
++twos;
}
- std::ptrdiff_t ones = deque.size() - twos;
+ int ones = deque.size() - twos;
assert(std::count(deque.begin(), deque.end(), 1) == ones);
assert(std::count(deque.begin(), deque.end(), 2) == twos);
@@ -120,6 +120,17 @@ void test_deque_and_join_view_iterators() {
assert(std::count(joined.begin(), joined.end(), 2) == 4);
assert(std::count(joined.begin(), joined.end(), 99) == 0);
}
+
+ {
+ // Verify that join_view of vectors work properly
+ std::vector<std::vector<int>> vector = {{}, {0}, {1, 2}, {}, {0, 1, 2}, {0, 1, 2, 0}, {1}, {2, 0, 1}};
+ auto joined = vector | std::views::join;
+
+ assert(std::count(joined.begin(), joined.end(), 0) == 5);
+ assert(std::count(joined.begin(), joined.end(), 1) == 5);
+ assert(std::count(joined.begin(), joined.end(), 2) == 4);
+ assert(std::count(joined.begin(), joined.end(), 99) == 0);
+ }
#endif // TEST_STD_VER >= 20
}
@@ -129,7 +140,7 @@ int main(int, char**) {
static_assert(test());
#endif
#if TEST_STD_VER >= 11
- test_deque_and_join_view_iterators();
+ test_segmented_iterators();
#endif
return 0;
>From f42262a74d7419c4916db15b4c37a99740ff7873 Mon Sep 17 00:00:00 2001
From: Aryan Naraghi <aryan.naraghi at gmail.com>
Date: Mon, 18 May 2026 11:52:56 -0700
Subject: [PATCH 5/7] Adds std::count_if tests for segmented iterators
---
.../alg.nonmodifying/alg.count/count.pass.cpp | 2 +-
.../alg.count/count_if.pass.cpp | 92 ++++++++++++++-----
2 files changed, 69 insertions(+), 25 deletions(-)
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.count/count.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.count/count.pass.cpp
index ce88f14425ab5..88d912600de94 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.count/count.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.count/count.pass.cpp
@@ -21,13 +21,13 @@
#include <cstddef>
#include <cstdint>
#include <deque>
+#include <list>
#include <vector>
#include "sized_allocator.h"
#include "test_macros.h"
#include "test_iterators.h"
#include "type_algorithms.h"
-#include <list>
struct Test {
template <class Iter>
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.count/count_if.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.count/count_if.pass.cpp
index 64c3d7ce3ac86..9672129690481 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.count/count_if.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.count/count_if.pass.cpp
@@ -15,44 +15,88 @@
#include <algorithm>
#include <cassert>
+#include <cstddef>
+#include <deque>
#include <functional>
#include <iterator>
+#include <list>
+#include <vector>
#include "test_macros.h"
#include "test_iterators.h"
struct eq {
- TEST_CONSTEXPR eq (int val) : v(val) {}
- TEST_CONSTEXPR bool operator () (int v2) const { return v == v2; }
- int v;
- };
+ TEST_CONSTEXPR eq(int val) : v(val) {}
+ TEST_CONSTEXPR bool operator()(int v2) const { return v == v2; }
+ int v;
+};
#if TEST_STD_VER > 17
TEST_CONSTEXPR bool test_constexpr() {
- int ia[] = {0, 1, 2, 2, 0, 1, 2, 3};
- int ib[] = {1, 2, 3, 4, 5, 6};
- return (std::count_if(std::begin(ia), std::end(ia), eq(2)) == 3)
- && (std::count_if(std::begin(ib), std::end(ib), eq(9)) == 0)
- ;
- }
+ int ia[] = {0, 1, 2, 2, 0, 1, 2, 3};
+ int ib[] = {1, 2, 3, 4, 5, 6};
+ return (std::count_if(std::begin(ia), std::end(ia), eq(2)) == 3) &&
+ (std::count_if(std::begin(ib), std::end(ib), eq(9)) == 0);
+}
#endif
-int main(int, char**)
-{
- int ia[] = {0, 1, 2, 2, 0, 1, 2, 3};
- const unsigned sa = sizeof(ia)/sizeof(ia[0]);
- assert(std::count_if(cpp17_input_iterator<const int*>(ia),
- cpp17_input_iterator<const int*>(ia + sa),
- eq(2)) == 3);
- assert(std::count_if(cpp17_input_iterator<const int*>(ia),
- cpp17_input_iterator<const int*>(ia + sa),
- eq(7)) == 0);
- assert(std::count_if(cpp17_input_iterator<const int*>(ia),
- cpp17_input_iterator<const int*>(ia),
- eq(2)) == 0);
+void test_segmented_iterators() {
+ {
+ // Verify that segmented deque iterators work properly
+ const int sizes[] = {0, 1, 2, 1023, 1024, 1025, 2047, 2048, 2049};
+ for (const int size : sizes) {
+ std::deque<int> deque(size, 1);
+
+ std::ptrdiff_t twos = 0;
+ for (int i = 0; i < size; i += 3) {
+ deque[i] = 2;
+ ++twos;
+ }
+ std::ptrdiff_t ones = deque.size() - twos;
+
+ assert(std::count_if(deque.begin(), deque.end(), eq(1)) == ones);
+ assert(std::count_if(deque.begin(), deque.end(), eq(2)) == twos);
+ assert(std::count_if(deque.begin(), deque.end(), eq(99)) == 0);
+ }
+ }
+
+#if TEST_STD_VER >= 20
+ {
+ // Verify that join_view of lists work properly
+ std::list<std::list<int>> list = {{}, {0}, {1, 2}, {}, {0, 1, 2}, {0, 1, 2, 0}, {1}, {2, 0, 1}};
+ auto joined = list | std::views::join;
+
+ assert(std::count_if(joined.begin(), joined.end(), eq(0)) == 5);
+ assert(std::count_if(joined.begin(), joined.end(), eq(1)) == 5);
+ assert(std::count_if(joined.begin(), joined.end(), eq(2)) == 4);
+ assert(std::count_if(joined.begin(), joined.end(), eq(99)) == 0);
+ }
+
+ {
+ // Verify that join_view of vectors work properly
+ std::vector<std::vector<int>> vector = {{}, {0}, {1, 2}, {}, {0, 1, 2}, {0, 1, 2, 0}, {1}, {2, 0, 1}};
+ auto joined = vector | std::views::join;
+
+ assert(std::count_if(joined.begin(), joined.end(), eq(0)) == 5);
+ assert(std::count_if(joined.begin(), joined.end(), eq(1)) == 5);
+ assert(std::count_if(joined.begin(), joined.end(), eq(2)) == 4);
+ assert(std::count_if(joined.begin(), joined.end(), eq(99)) == 0);
+ }
+#endif // TEST_STD_VER >= 20
+}
+
+int main(int, char**) {
+ int ia[] = {0, 1, 2, 2, 0, 1, 2, 3};
+ const unsigned sa = sizeof(ia) / sizeof(ia[0]);
+ assert(std::count_if(cpp17_input_iterator<const int*>(ia), cpp17_input_iterator<const int*>(ia + sa), eq(2)) == 3);
+ assert(std::count_if(cpp17_input_iterator<const int*>(ia), cpp17_input_iterator<const int*>(ia + sa), eq(7)) == 0);
+ assert(std::count_if(cpp17_input_iterator<const int*>(ia), cpp17_input_iterator<const int*>(ia), eq(2)) == 0);
#if TEST_STD_VER > 17
- static_assert(test_constexpr());
+ static_assert(test_constexpr());
+#endif
+#if TEST_STD_VER >= 11
+ test_segmented_iterators();
#endif
return 0;
>From 1027b0b1cd5fe3d4e897e49568e1b786abdeb124 Mon Sep 17 00:00:00 2001
From: Aryan Naraghi <aryan.naraghi at gmail.com>
Date: Tue, 19 May 2026 17:12:05 -0700
Subject: [PATCH 6/7] Reimplements count and count_if in terms of for_each
---
libcxx/include/__algorithm/count.h | 47 ++++++++++-----------------
libcxx/include/__algorithm/count_if.h | 33 ++++++-------------
2 files changed, 27 insertions(+), 53 deletions(-)
diff --git a/libcxx/include/__algorithm/count.h b/libcxx/include/__algorithm/count.h
index f1b230691584e..7b2e16d265f88 100644
--- a/libcxx/include/__algorithm/count.h
+++ b/libcxx/include/__algorithm/count.h
@@ -10,7 +10,7 @@
#ifndef _LIBCPP___ALGORITHM_COUNT_H
#define _LIBCPP___ALGORITHM_COUNT_H
-#include <__algorithm/for_each_segment.h>
+#include <__algorithm/for_each.h>
#include <__algorithm/iterator_operations.h>
#include <__algorithm/min.h>
#include <__bit/invert_if.h>
@@ -19,7 +19,6 @@
#include <__functional/identity.h>
#include <__fwd/bit_reference.h>
#include <__iterator/iterator_traits.h>
-#include <__iterator/segmented_iterator.h>
#include <__type_traits/enable_if.h>
#include <__type_traits/invoke.h>
@@ -36,31 +35,19 @@ _LIBCPP_BEGIN_NAMESPACE_STD
template <class _AlgPolicy, class _Iter, class _Sent, class _Tp, class _Proj>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 typename _IterOps<_AlgPolicy>::template __difference_type<_Iter>
__count(_Iter __first, _Sent __last, const _Tp& __value, _Proj& __proj) {
- typename _IterOps<_AlgPolicy>::template __difference_type<_Iter> __r(0);
- for (; __first != __last; ++__first)
- if (std::__invoke(__proj, *__first) == __value)
- ++__r;
- return __r;
-}
+ typename _IterOps<_AlgPolicy>::template __difference_type<_Iter> __counter(0);
+
+ auto __apply = [&__value, &__counter](auto&& __elem) {
+ if (__elem == __value) {
+ ++__counter;
+ }
+ };
-// segmented iterator implementation
-#ifndef _LIBCPP_CXX03_LANG
-template <class _AlgPolicy,
- class _SegmentedIterator,
- class _Tp,
- class _Proj,
- __enable_if_t<__is_segmented_iterator_v<_SegmentedIterator>, int> = 0>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20
-typename _IterOps<_AlgPolicy>::template __difference_type<_SegmentedIterator>
-__count(_SegmentedIterator __first, _SegmentedIterator __last, const _Tp& __value, _Proj& __proj) {
- typename _IterOps<_AlgPolicy>::template __difference_type<_SegmentedIterator> __r(0);
- using __local_iterator_t = typename __segmented_iterator_traits<_SegmentedIterator>::__local_iterator;
- std::__for_each_segment(__first, __last, [&](__local_iterator_t __lfirst, __local_iterator_t __llast) {
- __r += std::__count<_AlgPolicy>(__lfirst, __llast, __value, __proj);
- });
- return __r;
+ // We implement __count using __for_each to inherit its optimizations for
+ // segmented iterators. This improves performance without adding complexity.
+ std::__for_each(__first, __last, __apply, __proj);
+ return __counter;
}
-#endif // _LIBCPP_CXX03_LANG
// __bit_iterator implementation
template <bool _ToCount, class _Cp, bool _IsConst>
@@ -71,25 +58,25 @@ __count_bool(__bit_iterator<_Cp, _IsConst> __first, typename __size_difference_t
using difference_type = typename _It::difference_type;
const int __bits_per_word = _It::__bits_per_word;
- difference_type __r = 0;
+ difference_type __counter = 0;
// do first partial word
if (__first.__ctz_ != 0) {
__storage_type __clz_f = static_cast<__storage_type>(__bits_per_word - __first.__ctz_);
__storage_type __dn = std::min(__clz_f, __n);
__storage_type __m = std::__middle_mask<__storage_type>(__clz_f - __dn, __first.__ctz_);
- __r = std::__popcount(__storage_type(std::__invert_if<!_ToCount>(*__first.__seg_) & __m));
+ __counter = std::__popcount(__storage_type(std::__invert_if<!_ToCount>(*__first.__seg_) & __m));
__n -= __dn;
++__first.__seg_;
}
// do middle whole words
for (; __n >= __bits_per_word; ++__first.__seg_, __n -= __bits_per_word)
- __r += std::__popcount(std::__invert_if<!_ToCount>(*__first.__seg_));
+ __counter += std::__popcount(std::__invert_if<!_ToCount>(*__first.__seg_));
// do last partial word
if (__n > 0) {
__storage_type __m = std::__trailing_mask<__storage_type>(__bits_per_word - __n);
- __r += std::__popcount(__storage_type(std::__invert_if<!_ToCount>(*__first.__seg_) & __m));
+ __counter += std::__popcount(__storage_type(std::__invert_if<!_ToCount>(*__first.__seg_) & __m));
}
- return __r;
+ return __counter;
}
template <class, class _Cp, bool _IsConst, class _Tp, class _Proj, __enable_if_t<__is_identity<_Proj>::value, int> = 0>
diff --git a/libcxx/include/__algorithm/count_if.h b/libcxx/include/__algorithm/count_if.h
index 28329b948b687..26960259319b1 100644
--- a/libcxx/include/__algorithm/count_if.h
+++ b/libcxx/include/__algorithm/count_if.h
@@ -10,12 +10,11 @@
#ifndef _LIBCPP___ALGORITHM_COUNT_IF_H
#define _LIBCPP___ALGORITHM_COUNT_IF_H
-#include <__algorithm/for_each_segment.h>
+#include <__algorithm/for_each.h>
#include <__algorithm/iterator_operations.h>
#include <__config>
#include <__functional/identity.h>
#include <__iterator/iterator_traits.h>
-#include <__iterator/segmented_iterator.h>
#include <__type_traits/invoke.h>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -25,33 +24,21 @@
_LIBCPP_BEGIN_NAMESPACE_STD
template <class _AlgPolicy, class _Iter, class _Sent, class _Proj, class _Pred>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 __policy_iter_diff_t<_AlgPolicy, _Iter>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __policy_iter_diff_t<_AlgPolicy, _Iter>
__count_if(_Iter __first, _Sent __last, _Pred& __pred, _Proj& __proj) {
__policy_iter_diff_t<_AlgPolicy, _Iter> __counter(0);
- for (; __first != __last; ++__first) {
- if (std::__invoke(__pred, std::__invoke(__proj, *__first)))
+
+ auto __apply = [&__pred, &__counter](auto&& __elem) {
+ if (std::__invoke(__pred, __elem)) {
++__counter;
- }
- return __counter;
-}
+ }
+ };
-// segmented iterator implementation
-#ifndef _LIBCPP_CXX03_LANG
-template <class _AlgPolicy,
- class _SegmentedIterator,
- class _Proj,
- class _Pred,
- __enable_if_t<__is_segmented_iterator_v<_SegmentedIterator>, int> = 0>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 __policy_iter_diff_t<_AlgPolicy, _SegmentedIterator>
-__count_if(_SegmentedIterator __first, _SegmentedIterator __last, _Pred& __pred, _Proj& __proj) {
- __policy_iter_diff_t<_AlgPolicy, _SegmentedIterator> __counter(0);
- using __local_iterator_t = typename __segmented_iterator_traits<_SegmentedIterator>::__local_iterator;
- std::__for_each_segment(__first, __last, [&](__local_iterator_t __lfirst, __local_iterator_t __llast) {
- __counter += std::__count_if<_AlgPolicy>(__lfirst, __llast, __pred, __proj);
- });
+ // We implement __count_if using __for_each to inherit its optimizations for
+ // segmented iterators. This improves performance without adding complexity.
+ std::__for_each(__first, __last, __apply, __proj);
return __counter;
}
-#endif // _LIBCPP_CXX03_LANG
template <class _InputIterator, class _Predicate>
[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20
>From d5975eaa4c5f33fea67bbd812b5de32cb65fcf78 Mon Sep 17 00:00:00 2001
From: Aryan Naraghi <aryan.naraghi at gmail.com>
Date: Tue, 19 May 2026 17:16:06 -0700
Subject: [PATCH 7/7] Passed the iterators by rvalue
---
libcxx/include/__algorithm/count.h | 4 ++--
libcxx/include/__algorithm/count_if.h | 3 ++-
2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/libcxx/include/__algorithm/count.h b/libcxx/include/__algorithm/count.h
index 7b2e16d265f88..64d48e4d6c7f0 100644
--- a/libcxx/include/__algorithm/count.h
+++ b/libcxx/include/__algorithm/count.h
@@ -20,7 +20,7 @@
#include <__fwd/bit_reference.h>
#include <__iterator/iterator_traits.h>
#include <__type_traits/enable_if.h>
-#include <__type_traits/invoke.h>
+#include <__utility/move.h>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
@@ -45,7 +45,7 @@ __count(_Iter __first, _Sent __last, const _Tp& __value, _Proj& __proj) {
// We implement __count using __for_each to inherit its optimizations for
// segmented iterators. This improves performance without adding complexity.
- std::__for_each(__first, __last, __apply, __proj);
+ std::__for_each(std::move(__first), std::move(__last), __apply, __proj);
return __counter;
}
diff --git a/libcxx/include/__algorithm/count_if.h b/libcxx/include/__algorithm/count_if.h
index 26960259319b1..986a9d37720c8 100644
--- a/libcxx/include/__algorithm/count_if.h
+++ b/libcxx/include/__algorithm/count_if.h
@@ -16,6 +16,7 @@
#include <__functional/identity.h>
#include <__iterator/iterator_traits.h>
#include <__type_traits/invoke.h>
+#include <__utility/move.h>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
@@ -36,7 +37,7 @@ __count_if(_Iter __first, _Sent __last, _Pred& __pred, _Proj& __proj) {
// We implement __count_if using __for_each to inherit its optimizations for
// segmented iterators. This improves performance without adding complexity.
- std::__for_each(__first, __last, __apply, __proj);
+ std::__for_each(std::move(__first), std::move(__last), __apply, __proj);
return __counter;
}
More information about the libcxx-commits
mailing list