[libcxx-commits] [libcxx] [libc++] Fix ambiguous call in {ranges, std}::find (PR #122641)

Peng Liu via libcxx-commits libcxx-commits at lists.llvm.org
Sun Jan 12 11:50:44 PST 2025


https://github.com/winner245 updated https://github.com/llvm/llvm-project/pull/122641

>From 58b3e49f8f603ca67a5bfbe6859b841a740ac079 Mon Sep 17 00:00:00 2001
From: Peng Liu <winner245 at hotmail.com>
Date: Fri, 10 Jan 2025 15:19:08 -0500
Subject: [PATCH] Fix ambiguous call in {ranges, std}::find

---
 libcxx/include/__algorithm/find.h             | 10 +-
 libcxx/include/__bit/countr.h                 | 94 ++++++++++++++++---
 libcxx/include/__fwd/bit_reference.h          | 18 ++++
 .../alg.nonmodifying/alg.find/find.pass.cpp   | 30 ++++++
 .../alg.find/ranges.find.pass.cpp             | 70 ++++++++++----
 libcxx/test/support/sized_allocator.h         | 58 ++++++++++++
 6 files changed, 245 insertions(+), 35 deletions(-)
 create mode 100644 libcxx/test/support/sized_allocator.h

diff --git a/libcxx/include/__algorithm/find.h b/libcxx/include/__algorithm/find.h
index a05d50718595ee..78b1a8a1a27cb3 100644
--- a/libcxx/include/__algorithm/find.h
+++ b/libcxx/include/__algorithm/find.h
@@ -106,10 +106,10 @@ __find_bool(__bit_iterator<_Cp, _IsConst> __first, typename _Cp::size_type __n)
   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     = (~__storage_type(0) << __first.__ctz_) & (~__storage_type(0) >> (__clz_f - __dn));
+    __storage_type __m     = std::__middle_mask<__storage_type>(__first.__ctz_, __clz_f - __dn);
     __storage_type __b     = std::__invert_if<!_ToFind>(*__first.__seg_) & __m;
     if (__b)
-      return _It(__first.__seg_, static_cast<unsigned>(std::__libcpp_ctz(__b)));
+      return _It(__first.__seg_, static_cast<unsigned>(std::__countr_zero(__b)));
     if (__n == __dn)
       return __first + __n;
     __n -= __dn;
@@ -119,14 +119,14 @@ __find_bool(__bit_iterator<_Cp, _IsConst> __first, typename _Cp::size_type __n)
   for (; __n >= __bits_per_word; ++__first.__seg_, __n -= __bits_per_word) {
     __storage_type __b = std::__invert_if<!_ToFind>(*__first.__seg_);
     if (__b)
-      return _It(__first.__seg_, static_cast<unsigned>(std::__libcpp_ctz(__b)));
+      return _It(__first.__seg_, static_cast<unsigned>(std::__countr_zero(__b)));
   }
   // do last partial word
   if (__n > 0) {
-    __storage_type __m = ~__storage_type(0) >> (__bits_per_word - __n);
+    __storage_type __m = std::__trailing_mask<__storage_type>(__bits_per_word - __n);
     __storage_type __b = std::__invert_if<!_ToFind>(*__first.__seg_) & __m;
     if (__b)
-      return _It(__first.__seg_, static_cast<unsigned>(std::__libcpp_ctz(__b)));
+      return _It(__first.__seg_, static_cast<unsigned>(std::__countr_zero(__b)));
   }
   return _It(__first.__seg_, static_cast<unsigned>(__n));
 }
diff --git a/libcxx/include/__bit/countr.h b/libcxx/include/__bit/countr.h
index 2f7571133bd03a..698c959d920b87 100644
--- a/libcxx/include/__bit/countr.h
+++ b/libcxx/include/__bit/countr.h
@@ -15,6 +15,8 @@
 #include <__bit/rotate.h>
 #include <__concepts/arithmetic.h>
 #include <__config>
+#include <__type_traits/enable_if.h>
+#include <__type_traits/is_unsigned.h>
 #include <limits>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -38,20 +40,19 @@ _LIBCPP_BEGIN_NAMESPACE_STD
   return __builtin_ctzll(__x);
 }
 
-template <class _Tp>
-[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 int __countr_zero(_Tp __t) _NOEXCEPT {
-#if __has_builtin(__builtin_ctzg)
-  return __builtin_ctzg(__t, numeric_limits<_Tp>::digits);
-#else  // __has_builtin(__builtin_ctzg)
-  if (__t == 0)
-    return numeric_limits<_Tp>::digits;
-  if (sizeof(_Tp) <= sizeof(unsigned int))
+#if _LIBCPP_STD_VER >= 17
+// Implementation using constexpr if for C++ standards >= 17
+
+// Precondition: __t != 0 (This is guaranteed by the caller __countr_zero, which handles __t == 0 as a special case)
+template <class _Tp, __enable_if_t<is_unsigned<_Tp>::value, int> = 0>
+[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 int __countr_zero_impl(_Tp __t) _NOEXCEPT {
+  if constexpr (sizeof(_Tp) <= sizeof(unsigned int)) {
     return std::__libcpp_ctz(static_cast<unsigned int>(__t));
-  else if (sizeof(_Tp) <= sizeof(unsigned long))
+  } else if constexpr (sizeof(_Tp) <= sizeof(unsigned long)) {
     return std::__libcpp_ctz(static_cast<unsigned long>(__t));
-  else if (sizeof(_Tp) <= sizeof(unsigned long long))
+  } else if constexpr (sizeof(_Tp) <= sizeof(unsigned long long)) {
     return std::__libcpp_ctz(static_cast<unsigned long long>(__t));
-  else {
+  } else {
     int __ret                      = 0;
     const unsigned int __ulldigits = numeric_limits<unsigned long long>::digits;
     while (static_cast<unsigned long long>(__t) == 0uLL) {
@@ -60,7 +61,76 @@ template <class _Tp>
     }
     return __ret + std::__libcpp_ctz(static_cast<unsigned long long>(__t));
   }
-#endif // __has_builtin(__builtin_ctzg)
+}
+
+#else
+// Equivalent SFINAE-based implementation for older C++ standards < 17
+
+// Precondition: __t != 0 (This is guaranteed by the caller __countr_zero, which handles __t == 0 as a special case)
+template < class _Tp, __enable_if_t<is_unsigned<_Tp>::value && sizeof(_Tp) <= sizeof(unsigned int), int> = 0>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __countr_zero_impl(_Tp __t) _NOEXCEPT {
+  return std::__libcpp_ctz(static_cast<unsigned int>(__t));
+}
+
+// Precondition: __t != 0 (This is guaranteed by the caller __countr_zero)
+template < class _Tp,
+           __enable_if_t<is_unsigned<_Tp>::value && (sizeof(_Tp) > sizeof(unsigned int)) &&
+                             sizeof(_Tp) <= sizeof(unsigned long),
+                         int> = 0 >
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __countr_zero_impl(_Tp __t) _NOEXCEPT {
+  return std::__libcpp_ctz(static_cast<unsigned long>(__t));
+}
+
+// Precondition: __t != 0 (This is guaranteed by the caller __countr_zero)
+template < class _Tp,
+           __enable_if_t<is_unsigned<_Tp>::value && (sizeof(_Tp) > sizeof(unsigned long)) &&
+                             sizeof(_Tp) <= sizeof(unsigned long long),
+                         int> = 0 >
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __countr_zero_impl(_Tp __t) _NOEXCEPT {
+  return std::__libcpp_ctz(static_cast<unsigned long long>(__t));
+}
+
+#  if _LIBCPP_STD_VER == 11
+
+// Recursive constexpr implementation for C++11 due to limited constexpr support
+// Precondition: __t != 0 (This is guaranteed by the caller __countr_zero)
+template < class _Tp, __enable_if_t<is_unsigned<_Tp>::value && (sizeof(_Tp) > sizeof(unsigned long long)), int> = 0 >
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __countr_zero_impl(_Tp __t) _NOEXCEPT {
+  unsigned long long __ull       = static_cast<unsigned long long>(__t);
+  const unsigned int __ulldigits = numeric_limits<unsigned long long>::digits;
+
+  // Explicitly handle the __t == 0 case to suppress clang-diagnostic-shift-count-overflow warning.
+  return __t == 0 ? numeric_limits<_Tp>::digits
+                  : (__ull == 0ull ? __ulldigits + std::__countr_zero_impl<_Tp>(__ull >> __ulldigits)
+                                   : std::__libcpp_ctz(__ull));
+}
+
+#  else
+
+// Loop-based constexpr implementation for C++14 (and non-constexpr for C++03, 98)
+// Precondition: __t != 0 (This is guaranteed by the caller __countr_zero)
+template < class _Tp, __enable_if_t<is_unsigned<_Tp>::value && (sizeof(_Tp) > sizeof(unsigned long long)), int> = 0 >
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 int __countr_zero_impl(_Tp __t) _NOEXCEPT {
+  int __ret                      = 0;
+  const unsigned int __ulldigits = numeric_limits<unsigned long long>::digits;
+  while (static_cast<unsigned long long>(__t) == 0uLL) {
+    __ret += __ulldigits;
+    __t >>= __ulldigits;
+  }
+  return __ret + std::__libcpp_ctz(static_cast<unsigned long long>(__t));
+}
+
+#  endif // _LIBCPP_STD_VER == 11
+
+#endif // _LIBCPP_STD_VER >= 17
+
+template <class _Tp>
+[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __countr_zero(_Tp __t) _NOEXCEPT {
+#if __has_builtin(__builtin_ctzg)
+  return __builtin_ctzg(__t, numeric_limits<_Tp>::digits);
+#else
+  return __t != 0 ? __countr_zero_impl(__t) : numeric_limits<_Tp>::digits;
+#endif
 }
 
 #if _LIBCPP_STD_VER >= 20
diff --git a/libcxx/include/__fwd/bit_reference.h b/libcxx/include/__fwd/bit_reference.h
index 237efb6db66429..c927e65747af08 100644
--- a/libcxx/include/__fwd/bit_reference.h
+++ b/libcxx/include/__fwd/bit_reference.h
@@ -10,6 +10,8 @@
 #define _LIBCPP___FWD_BIT_REFERENCE_H
 
 #include <__config>
+#include <__type_traits/enable_if.h>
+#include <__type_traits/is_unsigned.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -20,6 +22,22 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 template <class _Cp, bool _IsConst, typename _Cp::__storage_type = 0>
 class __bit_iterator;
 
+template <class _StorageType, __enable_if_t<is_unsigned<_StorageType>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _StorageType __leading_mask(unsigned __shift) {
+  return static_cast<_StorageType>(static_cast<_StorageType>(~static_cast<_StorageType>(0)) << __shift);
+}
+
+template <class _StorageType, __enable_if_t<is_unsigned<_StorageType>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _StorageType __trailing_mask(unsigned __shift) {
+  return static_cast<_StorageType>(static_cast<_StorageType>(~static_cast<_StorageType>(0)) >> __shift);
+}
+
+template <class _StorageType, __enable_if_t<is_unsigned<_StorageType>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _StorageType __middle_mask(unsigned __lshift, unsigned __rshift) {
+  return static_cast<_StorageType>(
+      std::__leading_mask<_StorageType>(__lshift) & std::__trailing_mask<_StorageType>(__rshift));
+}
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP___FWD_BIT_REFERENCE_H
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/find.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/find.pass.cpp
index c41246522fdeba..6940ff97a0ba5b 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/find.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/find.pass.cpp
@@ -14,6 +14,7 @@
 // MSVC warning C4389: '==': signed/unsigned mismatch
 // MSVC warning C4805: '==': unsafe mix of type 'char' and type 'bool' in operation
 // ADDITIONAL_COMPILE_FLAGS(cl-style-warnings): /wd4245 /wd4305 /wd4310 /wd4389 /wd4805
+// XFAIL: FROZEN-CXX03-HEADERS-FIXME
 
 // <algorithm>
 
@@ -28,6 +29,7 @@
 #include <vector>
 #include <type_traits>
 
+#include "sized_allocator.h"
 #include "test_macros.h"
 #include "test_iterators.h"
 #include "type_algorithms.h"
@@ -206,6 +208,33 @@ struct TestIntegerPromotions {
   }
 };
 
+TEST_CONSTEXPR_CXX20 void test_bititer_with_custom_sized_types() {
+  {
+    using Alloc = sized_allocator<bool, std::uint8_t, std::int8_t>;
+    std::vector<bool, Alloc> in(100, false, Alloc(1));
+    in[in.size() - 2] = true;
+    assert(std::find(in.begin(), in.end(), true) == in.end() - 2);
+  }
+  {
+    using Alloc = sized_allocator<bool, std::uint16_t, std::int16_t>;
+    std::vector<bool, Alloc> in(200, false, Alloc(1));
+    in[in.size() - 2] = true;
+    assert(std::find(in.begin(), in.end(), true) == in.end() - 2);
+  }
+  {
+    using Alloc = sized_allocator<bool, std::uint32_t, std::int32_t>;
+    std::vector<bool, Alloc> in(200, false, Alloc(1));
+    in[in.size() - 2] = true;
+    assert(std::find(in.begin(), in.end(), true) == in.end() - 2);
+  }
+  {
+    using Alloc = sized_allocator<bool, std::uint64_t, std::int64_t>;
+    std::vector<bool, Alloc> in(200, false, Alloc(1));
+    in[in.size() - 2] = true;
+    assert(std::find(in.begin(), in.end(), true) == in.end() - 2);
+  }
+}
+
 TEST_CONSTEXPR_CXX20 bool test() {
   types::for_each(types::integer_types(), TestTypes<char>());
   types::for_each(types::integer_types(), TestTypes<int>());
@@ -226,6 +255,7 @@ TEST_CONSTEXPR_CXX20 bool test() {
 #endif
 
   types::for_each(types::integral_types(), TestIntegerPromotions());
+  test_bititer_with_custom_sized_types();
 
   return true;
 }
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/ranges.find.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/ranges.find.pass.cpp
index 4ae049c3ec0011..a2af568be37c04 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/ranges.find.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/ranges.find.pass.cpp
@@ -31,6 +31,7 @@
 #include <vector>
 
 #include "almost_satisfies_types.h"
+#include "sized_allocator.h"
 #include "test_iterators.h"
 
 struct NotEqualityComparable {};
@@ -66,14 +67,14 @@ constexpr void test_iterators() {
   using ValueT = std::iter_value_t<It>;
   { // simple test
     {
-      ValueT a[] = {1, 2, 3, 4};
+      ValueT a[]                = {1, 2, 3, 4};
       std::same_as<It> auto ret = std::ranges::find(It(a), Sent(It(a + 4)), 4);
       assert(base(ret) == a + 3);
       assert(*ret == 4);
     }
     {
-      ValueT a[] = {1, 2, 3, 4};
-      auto range = std::ranges::subrange(It(a), Sent(It(a + 4)));
+      ValueT a[]                = {1, 2, 3, 4};
+      auto range                = std::ranges::subrange(It(a), Sent(It(a + 4)));
       std::same_as<It> auto ret = std::ranges::find(range, 4);
       assert(base(ret) == a + 3);
       assert(*ret == 4);
@@ -83,13 +84,13 @@ constexpr void test_iterators() {
   { // check that an empty range works
     {
       std::array<ValueT, 0> a = {};
-      auto ret = std::ranges::find(It(a.data()), Sent(It(a.data())), 1);
+      auto ret                = std::ranges::find(It(a.data()), Sent(It(a.data())), 1);
       assert(base(ret) == a.data());
     }
     {
       std::array<ValueT, 0> a = {};
-      auto range = std::ranges::subrange(It(a.data()), Sent(It(a.data())));
-      auto ret = std::ranges::find(range, 1);
+      auto range              = std::ranges::subrange(It(a.data()), Sent(It(a.data())));
+      auto ret                = std::ranges::find(range, 1);
       assert(base(ret) == a.data());
     }
   }
@@ -97,12 +98,12 @@ constexpr void test_iterators() {
   { // check that last is returned with no match
     {
       ValueT a[] = {1, 1, 1};
-      auto ret = std::ranges::find(a, a + 3, 0);
+      auto ret   = std::ranges::find(a, a + 3, 0);
       assert(ret == a + 3);
     }
     {
       ValueT a[] = {1, 1, 1};
-      auto ret = std::ranges::find(a, 0);
+      auto ret   = std::ranges::find(a, 0);
       assert(ret == a + 3);
     }
   }
@@ -120,6 +121,33 @@ class TriviallyComparable {
   bool operator==(const TriviallyComparable&) const = default;
 };
 
+constexpr void test_bititer_with_custom_sized_types() {
+  {
+    using Alloc = sized_allocator<bool, std::uint8_t, std::int8_t>;
+    std::vector<bool, Alloc> in(100, false, Alloc(1));
+    in[in.size() - 2] = true;
+    assert(std::ranges::find(in, true) == in.end() - 2);
+  }
+  {
+    using Alloc = sized_allocator<bool, std::uint16_t, std::int16_t>;
+    std::vector<bool, Alloc> in(200, false, Alloc(1));
+    in[in.size() - 2] = true;
+    assert(std::ranges::find(in, true) == in.end() - 2);
+  }
+  {
+    using Alloc = sized_allocator<bool, std::uint32_t, std::int32_t>;
+    std::vector<bool, Alloc> in(200, false, Alloc(1));
+    in[in.size() - 2] = true;
+    assert(std::ranges::find(in, true) == in.end() - 2);
+  }
+  {
+    using Alloc = sized_allocator<bool, std::uint64_t, std::int64_t>;
+    std::vector<bool, Alloc> in(200, false, Alloc(1));
+    in[in.size() - 2] = true;
+    assert(std::ranges::find(in, true) == in.end() - 2);
+  }
+}
+
 constexpr bool test() {
   types::for_each(types::type_list<char, wchar_t, int, long, TriviallyComparable<char>, TriviallyComparable<wchar_t>>{},
                   []<class T> {
@@ -148,7 +176,7 @@ constexpr bool test() {
         int comp;
         int other;
       };
-      S a[] = { {0, 0}, {0, 2}, {0, 1} };
+      S a[]    = {{0, 0}, {0, 2}, {0, 1}};
       auto ret = std::ranges::find(a, 0, &S::comp);
       assert(ret == a);
       assert(ret->comp == 0);
@@ -159,7 +187,7 @@ constexpr bool test() {
         int comp;
         int other;
       };
-      S a[] = { {0, 0}, {0, 2}, {0, 1} };
+      S a[]    = {{0, 0}, {0, 2}, {0, 1}};
       auto ret = std::ranges::find(a, a + 3, 0, &S::comp);
       assert(ret == a);
       assert(ret->comp == 0);
@@ -169,7 +197,7 @@ constexpr bool test() {
 
   {
     // check that an iterator is returned with a borrowing range
-    int a[] = {1, 2, 3, 4};
+    int a[]                     = {1, 2, 3, 4};
     std::same_as<int*> auto ret = std::ranges::find(std::views::all(a), 1);
     assert(ret == a);
     assert(*ret == 1);
@@ -178,23 +206,31 @@ constexpr bool test() {
   {
     // count invocations of the projection
     {
-      int a[] = {1, 2, 3, 4};
+      int a[]              = {1, 2, 3, 4};
       int projection_count = 0;
-      auto ret = std::ranges::find(a, a + 4, 2, [&](int i) { ++projection_count; return i; });
+      auto ret             = std::ranges::find(a, a + 4, 2, [&](int i) {
+        ++projection_count;
+        return i;
+      });
       assert(ret == a + 1);
       assert(*ret == 2);
       assert(projection_count == 2);
     }
     {
-      int a[] = {1, 2, 3, 4};
+      int a[]              = {1, 2, 3, 4};
       int projection_count = 0;
-      auto ret = std::ranges::find(a, 2, [&](int i) { ++projection_count; return i; });
+      auto ret             = std::ranges::find(a, 2, [&](int i) {
+        ++projection_count;
+        return i;
+      });
       assert(ret == a + 1);
       assert(*ret == 2);
       assert(projection_count == 2);
     }
   }
 
+  test_bititer_with_custom_sized_types();
+
   return true;
 }
 
@@ -210,9 +246,7 @@ class Comparable {
           return size;
         }()) {}
 
-  bool operator==(const Comparable& other) const {
-    return comparable_data[other.index_] == comparable_data[index_];
-  }
+  bool operator==(const Comparable& other) const { return comparable_data[other.index_] == comparable_data[index_]; }
 
   friend bool operator==(const Comparable& lhs, long long rhs) { return comparable_data[lhs.index_] == rhs; }
 };
diff --git a/libcxx/test/support/sized_allocator.h b/libcxx/test/support/sized_allocator.h
new file mode 100644
index 00000000000000..a877252e82962c
--- /dev/null
+++ b/libcxx/test/support/sized_allocator.h
@@ -0,0 +1,58 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef TEST_SUPPORT_SIZED_ALLOCATOR_H
+#define TEST_SUPPORT_SIZED_ALLOCATOR_H
+
+#include <cstddef>
+#include <limits>
+#include <memory>
+#include <new>
+
+#include "test_macros.h"
+
+template <typename T, typename SIZE_TYPE = std::size_t, typename DIFF_TYPE = std::ptrdiff_t>
+class sized_allocator {
+  template <typename U, typename Sz, typename Diff>
+  friend class sized_allocator;
+
+public:
+  using value_type                  = T;
+  using size_type                   = SIZE_TYPE;
+  using difference_type             = DIFF_TYPE;
+  using propagate_on_container_swap = std::true_type;
+
+  TEST_CONSTEXPR_CXX20 explicit sized_allocator(int d = 0) : data_(d) {}
+
+  template <typename U, typename Sz, typename Diff>
+  TEST_CONSTEXPR_CXX20 sized_allocator(const sized_allocator<U, Sz, Diff>& a) TEST_NOEXCEPT : data_(a.data_) {}
+
+  TEST_CONSTEXPR_CXX20 T* allocate(size_type n) {
+    if (n > max_size())
+      TEST_THROW(std::bad_array_new_length());
+    return std::allocator<T>().allocate(n);
+  }
+
+  TEST_CONSTEXPR_CXX20 void deallocate(T* p, size_type n) TEST_NOEXCEPT { std::allocator<T>().deallocate(p, n); }
+
+  TEST_CONSTEXPR size_type max_size() const TEST_NOEXCEPT {
+    return std::numeric_limits<size_type>::max() / sizeof(value_type);
+  }
+
+private:
+  int data_;
+
+  TEST_CONSTEXPR friend bool operator==(const sized_allocator& a, const sized_allocator& b) {
+    return a.data_ == b.data_;
+  }
+  TEST_CONSTEXPR friend bool operator!=(const sized_allocator& a, const sized_allocator& b) {
+    return a.data_ != b.data_;
+  }
+};
+
+#endif



More information about the libcxx-commits mailing list