[libcxx-commits] [libcxx] [libc++] Simplify std::equal a bit (PR #171585)

Nikolas Klauser via libcxx-commits libcxx-commits at lists.llvm.org
Thu Dec 11 06:02:26 PST 2025


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/171585

>From d7cdd49d4238054e874adbabb0ef7e449d7a3f5f Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Tue, 9 Dec 2025 12:21:52 +0100
Subject: [PATCH] [libc++] Simplify std::equal a bit

This is also a prerequisite for optimizing `std::search`.
---
 libcxx/include/__algorithm/equal.h            | 96 ++++++++-----------
 libcxx/include/__algorithm/ranges_equal.h     | 45 ++++-----
 libcxx/include/__bit_reference                | 16 +++-
 .../memory/specialized.algorithms/buffer.h    |  2 +-
 4 files changed, 74 insertions(+), 85 deletions(-)

diff --git a/libcxx/include/__algorithm/equal.h b/libcxx/include/__algorithm/equal.h
index 5a8c9504ede1d..63df043263cbe 100644
--- a/libcxx/include/__algorithm/equal.h
+++ b/libcxx/include/__algorithm/equal.h
@@ -160,22 +160,28 @@ template <class _Cp,
           bool _IsConst1,
           bool _IsConst2,
           class _BinaryPredicate,
-          __enable_if_t<__desugars_to_v<__equal_tag, _BinaryPredicate, bool, bool>, int> = 0>
+          class _Proj1,
+          class _Proj2,
+          __enable_if_t<__is_identity<_Proj1>::value && __is_identity<_Proj2>::value &&
+                            __desugars_to_v<__equal_tag, _BinaryPredicate, bool, bool>,
+                        int> = 0>
 [[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool __equal_iter_impl(
     __bit_iterator<_Cp, _IsConst1> __first1,
     __bit_iterator<_Cp, _IsConst1> __last1,
     __bit_iterator<_Cp, _IsConst2> __first2,
-    _BinaryPredicate) {
+    _BinaryPredicate,
+    _Proj1&,
+    _Proj2&) {
   if (__first1.__ctz_ == __first2.__ctz_)
     return std::__equal_aligned(__first1, __last1, __first2);
   return std::__equal_unaligned(__first1, __last1, __first2);
 }
 
-template <class _InputIterator1, class _InputIterator2, class _BinaryPredicate>
+template <class _InIter1, class _Sent1, class _InIter2, class _Pred, class _Proj1, class _Proj2>
 [[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool __equal_iter_impl(
-    _InputIterator1 __first1, _InputIterator1 __last1, _InputIterator2 __first2, _BinaryPredicate& __pred) {
+    _InIter1 __first1, _Sent1 __last1, _InIter2 __first2, _Pred& __pred, _Proj1& __proj1, _Proj2& __proj2) {
   for (; __first1 != __last1; ++__first1, (void)++__first2)
-    if (!__pred(*__first1, *__first2))
+    if (!std::__invoke(__pred, std::__invoke(__proj1, *__first1), std::__invoke(__proj2, *__first2)))
       return false;
   return true;
 }
@@ -183,19 +189,23 @@ template <class _InputIterator1, class _InputIterator2, class _BinaryPredicate>
 template <class _Tp,
           class _Up,
           class _BinaryPredicate,
-          __enable_if_t<__desugars_to_v<__equal_tag, _BinaryPredicate, _Tp, _Up> && !is_volatile<_Tp>::value &&
+          class _Proj1,
+          class _Proj2,
+          __enable_if_t<__is_identity<_Proj1>::value && __is_identity<_Proj2>::value &&
+                            __desugars_to_v<__equal_tag, _BinaryPredicate, _Tp, _Up> && !is_volatile<_Tp>::value &&
                             !is_volatile<_Up>::value && __libcpp_is_trivially_equality_comparable<_Tp, _Up>::value,
                         int> = 0>
 [[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool
-__equal_iter_impl(_Tp* __first1, _Tp* __last1, _Up* __first2, _BinaryPredicate&) {
+__equal_iter_impl(_Tp* __first1, _Tp* __last1, _Up* __first2, _BinaryPredicate&, _Proj1&, _Proj2&) {
   return std::__constexpr_memcmp_equal(__first1, __first2, __element_count(__last1 - __first1));
 }
 
 template <class _InputIterator1, class _InputIterator2, class _BinaryPredicate>
 [[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool
 equal(_InputIterator1 __first1, _InputIterator1 __last1, _InputIterator2 __first2, _BinaryPredicate __pred) {
+  __identity __proj;
   return std::__equal_iter_impl(
-      std::__unwrap_iter(__first1), std::__unwrap_iter(__last1), std::__unwrap_iter(__first2), __pred);
+      std::__unwrap_iter(__first1), std::__unwrap_iter(__last1), std::__unwrap_iter(__first2), __pred, __proj, __proj);
 }
 
 template <class _InputIterator1, class _InputIterator2>
@@ -206,52 +216,28 @@ equal(_InputIterator1 __first1, _InputIterator1 __last1, _InputIterator2 __first
 
 #if _LIBCPP_STD_VER >= 14
 
-template <class _Iter1, class _Sent1, class _Iter2, class _Sent2, class _Pred, class _Proj1, class _Proj2>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool __equal_impl(
-    _Iter1 __first1, _Sent1 __last1, _Iter2 __first2, _Sent2 __last2, _Pred& __comp, _Proj1& __proj1, _Proj2& __proj2) {
-  while (__first1 != __last1 && __first2 != __last2) {
-    if (!std::__invoke(__comp, std::__invoke(__proj1, *__first1), std::__invoke(__proj2, *__first2)))
-      return false;
-    ++__first1;
-    ++__first2;
-  }
-  return __first1 == __last1 && __first2 == __last2;
-}
-
-template <class _Tp,
-          class _Up,
-          class _Pred,
-          class _Proj1,
-          class _Proj2,
-          __enable_if_t<__desugars_to_v<__equal_tag, _Pred, _Tp, _Up> && __is_identity<_Proj1>::value &&
-                            __is_identity<_Proj2>::value && !is_volatile<_Tp>::value && !is_volatile<_Up>::value &&
-                            __libcpp_is_trivially_equality_comparable<_Tp, _Up>::value,
-                        int> = 0>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool
-__equal_impl(_Tp* __first1, _Tp* __last1, _Up* __first2, _Up*, _Pred&, _Proj1&, _Proj2&) {
-  return std::__constexpr_memcmp_equal(__first1, __first2, __element_count(__last1 - __first1));
-}
-
-template <class _Cp,
-          bool _IsConst1,
-          bool _IsConst2,
+template <bool __known_equal_length,
+          class _Iter1,
+          class _Sent1,
+          class _Iter2,
+          class _Sent2,
           class _Pred,
           class _Proj1,
-          class _Proj2,
-          __enable_if_t<__desugars_to_v<__equal_tag, _Pred, bool, bool> && __is_identity<_Proj1>::value &&
-                            __is_identity<_Proj2>::value,
-                        int> = 0>
+          class _Proj2>
 [[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool __equal_impl(
-    __bit_iterator<_Cp, _IsConst1> __first1,
-    __bit_iterator<_Cp, _IsConst1> __last1,
-    __bit_iterator<_Cp, _IsConst2> __first2,
-    __bit_iterator<_Cp, _IsConst2>,
-    _Pred&,
-    _Proj1&,
-    _Proj2&) {
-  if (__first1.__ctz_ == __first2.__ctz_)
-    return std::__equal_aligned(__first1, __last1, __first2);
-  return std::__equal_unaligned(__first1, __last1, __first2);
+    _Iter1 __first1, _Sent1 __last1, _Iter2 __first2, _Sent2 __last2, _Pred& __comp, _Proj1& __proj1, _Proj2& __proj2) {
+  if constexpr (__known_equal_length) {
+    return std::__equal_iter_impl(
+        std::move(__first1), std::move(__last1), std::move(__first2), __comp, __proj1, __proj2);
+  } else {
+    while (__first1 != __last1 && __first2 != __last2) {
+      if (!std::__invoke(__comp, std::__invoke(__proj1, *__first1), std::__invoke(__proj2, *__first2)))
+        return false;
+      ++__first1;
+      ++__first2;
+    }
+    return __first1 == __last1 && __first2 == __last2;
+  }
 }
 
 template <class _InputIterator1, class _InputIterator2, class _BinaryPredicate>
@@ -261,13 +247,15 @@ equal(_InputIterator1 __first1,
       _InputIterator2 __first2,
       _InputIterator2 __last2,
       _BinaryPredicate __pred) {
-  if constexpr (__has_random_access_iterator_category<_InputIterator1>::value &&
-                __has_random_access_iterator_category<_InputIterator2>::value) {
+  static constexpr bool __both_random_access =
+      __has_random_access_iterator_category<_InputIterator1>::value &&
+      __has_random_access_iterator_category<_InputIterator2>::value;
+  if constexpr (__both_random_access) {
     if (std::distance(__first1, __last1) != std::distance(__first2, __last2))
       return false;
   }
   __identity __proj;
-  return std::__equal_impl(
+  return std::__equal_impl<__both_random_access>(
       std::__unwrap_iter(__first1),
       std::__unwrap_iter(__last1),
       std::__unwrap_iter(__first2),
diff --git a/libcxx/include/__algorithm/ranges_equal.h b/libcxx/include/__algorithm/ranges_equal.h
index c26d13f002201..300ff13faf711 100644
--- a/libcxx/include/__algorithm/ranges_equal.h
+++ b/libcxx/include/__algorithm/ranges_equal.h
@@ -13,13 +13,12 @@
 #include <__algorithm/unwrap_range.h>
 #include <__config>
 #include <__functional/identity.h>
-#include <__functional/invoke.h>
 #include <__functional/ranges_operations.h>
 #include <__iterator/concepts.h>
-#include <__iterator/distance.h>
 #include <__iterator/indirectly_comparable.h>
 #include <__ranges/access.h>
 #include <__ranges/concepts.h>
+#include <__ranges/size.h>
 #include <__utility/move.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -35,6 +34,8 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 namespace ranges {
 struct __equal {
+
+
   template <input_iterator _Iter1,
             sentinel_for<_Iter1> _Sent1,
             input_iterator _Iter2,
@@ -51,20 +52,15 @@ struct __equal {
       _Pred __pred   = {},
       _Proj1 __proj1 = {},
       _Proj2 __proj2 = {}) const {
-    if constexpr (sized_sentinel_for<_Sent1, _Iter1> && sized_sentinel_for<_Sent2, _Iter2>) {
-      if (__last1 - __first1 != __last2 - __first2)
+    auto [__ufirst1, __ulast1] = std::__unwrap_range(std::move(__first1), std::move(__last1));
+    auto [__ufirst2, __ulast2] = std::__unwrap_range(std::move(__first2), std::move(__last2));
+    static constexpr bool __both_sized = sized_sentinel_for<_Sent1, _Iter1> && sized_sentinel_for<_Sent2, _Iter2>;
+    if constexpr (__both_sized) {
+      if (__ulast1 - __ufirst1 != __ulast2 - __ufirst2)
         return false;
     }
-    auto __unwrapped1 = std::__unwrap_range(std::move(__first1), std::move(__last1));
-    auto __unwrapped2 = std::__unwrap_range(std::move(__first2), std::move(__last2));
-    return std::__equal_impl(
-        std::move(__unwrapped1.first),
-        std::move(__unwrapped1.second),
-        std::move(__unwrapped2.first),
-        std::move(__unwrapped2.second),
-        __pred,
-        __proj1,
-        __proj2);
+    return std::__equal_impl<__both_sized>(
+        std::move(__ufirst1), std::move(__ulast1), std::move(__ufirst2), std::move(__ulast2), __pred, __proj1, __proj2);
   }
 
   template <input_range _Range1,
@@ -75,21 +71,16 @@ struct __equal {
     requires indirectly_comparable<iterator_t<_Range1>, iterator_t<_Range2>, _Pred, _Proj1, _Proj2>
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr bool operator()(
       _Range1&& __range1, _Range2&& __range2, _Pred __pred = {}, _Proj1 __proj1 = {}, _Proj2 __proj2 = {}) const {
-    if constexpr (sized_range<_Range1> && sized_range<_Range2>) {
-      if (ranges::distance(__range1) != ranges::distance(__range2))
+    static constexpr bool __both_sized = sized_range<_Range1> && sized_range<_Range2>;
+    if constexpr (__both_sized) {
+      if (ranges::size(__range1) != ranges::size(__range2))
         return false;
     }
-    auto __unwrapped1 = std::__unwrap_range(ranges::begin(__range1), ranges::end(__range1));
-    auto __unwrapped2 = std::__unwrap_range(ranges::begin(__range2), ranges::end(__range2));
-    return std::__equal_impl(
-        std::move(__unwrapped1.first),
-        std::move(__unwrapped1.second),
-        std::move(__unwrapped2.first),
-        std::move(__unwrapped2.second),
-        __pred,
-        __proj1,
-        __proj2);
-    return false;
+
+    auto [__ufirst1, __ulast1] = std::__unwrap_range(ranges::begin(__range1), ranges::end(__range1));
+    auto [__ufirst2, __ulast2] = std::__unwrap_range(ranges::begin(__range2), ranges::end(__range2));
+    return std::__equal_impl<__both_sized>(
+        std::move(__ufirst1), std::move(__ulast1), std::move(__ufirst2), std::move(__ulast2), __pred, __proj1, __proj2);
   }
 };
 
diff --git a/libcxx/include/__bit_reference b/libcxx/include/__bit_reference
index 00bbc4100ddb9..082a0575ee7dc 100644
--- a/libcxx/include/__bit_reference
+++ b/libcxx/include/__bit_reference
@@ -509,10 +509,20 @@ private:
             bool _IsConst1,
             bool _IsConst2,
             class _BinaryPredicate,
-            __enable_if_t<__desugars_to_v<__equal_tag, _BinaryPredicate, bool, bool>, int> >
+            class _Proj1,
+            class _Proj2,
+            __enable_if_t<__is_identity<_Proj1>::value && __is_identity<_Proj2>::value &&
+                              __desugars_to_v<__equal_tag, _BinaryPredicate, bool, bool>,
+                          int> >
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 friend bool __equal_iter_impl(
-      __bit_iterator<_Dp, _IsConst1>, __bit_iterator<_Dp, _IsConst1>, __bit_iterator<_Dp, _IsConst2>, _BinaryPredicate);
-  template <class _Dp,
+      __bit_iterator<_Dp, _IsConst1>,
+      __bit_iterator<_Dp, _IsConst1>,
+      __bit_iterator<_Dp, _IsConst2>,
+      _BinaryPredicate,
+      _Proj1&,
+      _Proj2&);
+  template <bool,
+            class _Dp,
             bool _IsConst1,
             bool _IsConst2,
             class _Pred,
diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/buffer.h b/libcxx/test/std/utilities/memory/specialized.algorithms/buffer.h
index 1114fdc68e724..072c0546cd7be 100644
--- a/libcxx/test/std/utilities/memory/specialized.algorithms/buffer.h
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/buffer.h
@@ -19,7 +19,7 @@ struct Buffer {
   const T* cbegin() const { return reinterpret_cast<const T*>(buffer); }
   const T* cend() const { return cbegin() + N; }
 
-  constexpr int size() const { return N; }
+  constexpr unsigned size() const { return N; }
 };
 
 #endif // LIBCPP_TEST_STD_UTILITIES_MEMORY_SPECIALIZED_ALGORITHMS_BUFFER_H



More information about the libcxx-commits mailing list