1515#include < __bit/rotate.h>
1616#include < __concepts/arithmetic.h>
1717#include < __config>
18+ #include < __type_traits/enable_if.h>
19+ #include < __type_traits/is_unsigned.h>
1820#include < limits>
1921
2022#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -38,20 +40,19 @@ _LIBCPP_BEGIN_NAMESPACE_STD
3840 return __builtin_ctzll (__x);
3941}
4042
41- template <class _Tp >
42- [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 int __countr_zero (_Tp __t ) _NOEXCEPT {
43- #if __has_builtin(__builtin_ctzg)
44- return __builtin_ctzg (__t , numeric_limits<_Tp>::digits);
45- #else // __has_builtin(__builtin_ctzg)
46- if (__t == 0 )
47- return numeric_limits<_Tp>::digits;
48- if (sizeof (_Tp) <= sizeof (unsigned int ))
43+ #if _LIBCPP_STD_VER >= 17
44+ // Implementation using constexpr if for C++ standards >= 17
45+
46+ // Precondition: __t != 0 (This is guaranteed by the caller __countr_zero, which handles __t == 0 as a special case)
47+ template <class _Tp , __enable_if_t <is_unsigned<_Tp>::value, int > = 0 >
48+ [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 int __countr_zero_impl (_Tp __t ) _NOEXCEPT {
49+ if constexpr (sizeof (_Tp) <= sizeof (unsigned int )) {
4950 return std::__libcpp_ctz (static_cast <unsigned int >(__t ));
50- else if (sizeof (_Tp) <= sizeof (unsigned long ))
51+ } else if constexpr (sizeof (_Tp) <= sizeof (unsigned long )) {
5152 return std::__libcpp_ctz (static_cast <unsigned long >(__t ));
52- else if (sizeof (_Tp) <= sizeof (unsigned long long ))
53+ } else if constexpr (sizeof (_Tp) <= sizeof (unsigned long long )) {
5354 return std::__libcpp_ctz (static_cast <unsigned long long >(__t ));
54- else {
55+ } else {
5556 int __ret = 0 ;
5657 const unsigned int __ulldigits = numeric_limits<unsigned long long >::digits;
5758 while (static_cast <unsigned long long >(__t ) == 0uLL) {
@@ -60,7 +61,72 @@ template <class _Tp>
6061 }
6162 return __ret + std::__libcpp_ctz (static_cast <unsigned long long >(__t ));
6263 }
63- #endif // __has_builtin(__builtin_ctzg)
64+ }
65+
66+ #else
67+ // Equivalent SFINAE-based implementation for older C++ standards < 17
68+
69+ // Precondition: __t != 0 (This is guaranteed by the caller __countr_zero, which handles __t == 0 as a special case)
70+ template < class _Tp , __enable_if_t <is_unsigned<_Tp>::value && sizeof (_Tp) <= sizeof (unsigned int ), int > = 0 >
71+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __countr_zero_impl (_Tp __t ) _NOEXCEPT {
72+ return std::__libcpp_ctz (static_cast <unsigned int >(__t ));
73+ }
74+
75+ // Precondition: __t != 0 (This is guaranteed by the caller __countr_zero)
76+ template < class _Tp ,
77+ __enable_if_t <is_unsigned<_Tp>::value && (sizeof (_Tp) > sizeof (unsigned int )) &&
78+ sizeof (_Tp) <= sizeof (unsigned long ),
79+ int > = 0 >
80+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __countr_zero_impl (_Tp __t ) _NOEXCEPT {
81+ return std::__libcpp_ctz (static_cast <unsigned long >(__t ));
82+ }
83+
84+ // Precondition: __t != 0 (This is guaranteed by the caller __countr_zero)
85+ template < class _Tp ,
86+ __enable_if_t <is_unsigned<_Tp>::value && (sizeof (_Tp) > sizeof (unsigned long )) &&
87+ sizeof (_Tp) <= sizeof (unsigned long long ),
88+ int > = 0 >
89+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __countr_zero_impl (_Tp __t ) _NOEXCEPT {
90+ return std::__libcpp_ctz (static_cast <unsigned long long >(__t ));
91+ }
92+
93+ # if _LIBCPP_STD_VER == 11
94+
95+ // Recursive constexpr implementation for C++11 due to limited constexpr support
96+ // Precondition: __t != 0 (This is guaranteed by the caller __countr_zero)
97+ template < class _Tp , __enable_if_t <is_unsigned<_Tp>::value && (sizeof (_Tp) > sizeof (unsigned long long )), int > = 0 >
98+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __countr_zero_impl (_Tp __t ) _NOEXCEPT {
99+ unsigned long long __ull = static_cast <unsigned long long >(__t );
100+ const unsigned int __ulldigits = numeric_limits<unsigned long long >::digits;
101+ return __ull == 0ull ? __ulldigits + std::__countr_zero_impl<_Tp>(__t >> __ulldigits) : std::__libcpp_ctz (__ull);
102+ }
103+
104+ # else
105+
106+ // Loop-based constexpr implementation for C++14 (and non-constexpr for C++03, 98)
107+ // Precondition: __t != 0 (This is guaranteed by the caller __countr_zero)
108+ template < class _Tp , __enable_if_t <is_unsigned<_Tp>::value && (sizeof (_Tp) > sizeof (unsigned long long )), int > = 0 >
109+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 int __countr_zero_impl (_Tp __t ) _NOEXCEPT {
110+ int __ret = 0 ;
111+ const unsigned int __ulldigits = numeric_limits<unsigned long long >::digits;
112+ while (static_cast <unsigned long long >(__t ) == 0uLL) {
113+ __ret += __ulldigits;
114+ __t >>= __ulldigits;
115+ }
116+ return __ret + std::__libcpp_ctz (static_cast <unsigned long long >(__t ));
117+ }
118+
119+ # endif // _LIBCPP_STD_VER == 11
120+
121+ #endif // _LIBCPP_STD_VER >= 17
122+
123+ template <class _Tp , __enable_if_t <is_unsigned<_Tp>::value, int > = 0 >
124+ [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR int __countr_zero (_Tp __t ) _NOEXCEPT {
125+ #if __has_builtin(__builtin_ctzg)
126+ return __builtin_ctzg (__t , numeric_limits<_Tp>::digits);
127+ #else
128+ return __t != 0 ? __countr_zero_impl (__t ) : numeric_limits<_Tp>::digits;
129+ #endif
64130}
65131
66132#if _LIBCPP_STD_VER >= 20
0 commit comments