Simplify absl::strings_internal::IsStrictlyBaseOfAndConvertibleToSTLContainer

PiperOrigin-RevId: 929910722
Change-Id: I24f4d01582bff54232f64bbc2c9b8ce379332806
diff --git a/absl/strings/internal/stl_type_traits.h b/absl/strings/internal/stl_type_traits.h
index ae8454d..6298921 100644
--- a/absl/strings/internal/stl_type_traits.h
+++ b/absl/strings/internal/stl_type_traits.h
@@ -25,6 +25,8 @@
 #ifndef ABSL_STRINGS_INTERNAL_STL_TYPE_TRAITS_H_
 #define ABSL_STRINGS_INTERNAL_STL_TYPE_TRAITS_H_
 
+#include <stddef.h>
+
 #include <array>
 #include <bitset>
 #include <deque>
@@ -43,212 +45,97 @@
 ABSL_NAMESPACE_BEGIN
 namespace strings_internal {
 
-template <typename C, template <typename...> class T>
-struct IsSpecializationImpl : std::false_type {};
-template <template <typename...> class T, typename... Args>
-struct IsSpecializationImpl<T<Args...>, T> : std::true_type {};
-template <typename C, template <typename...> class T>
-using IsSpecialization = IsSpecializationImpl<std::decay_t<C>, T>;
+template <typename To, typename From>
+using IsCastableToDerivedSTLContainer =
+    std::enable_if_t<!std::is_same_v<From, To>, std::is_convertible<From, To>>;
 
 template <typename C>
-struct IsArrayImpl : std::false_type {};
-template <template <typename, size_t> class A, typename T, size_t N>
-struct IsArrayImpl<A<T, N>> : std::is_same<A<T, N>, std::array<T, N>> {};
-template <typename C>
-using IsArray = IsArrayImpl<std::decay_t<C>>;
+std::false_type CastableToDerivedSTLContainer(C*);
 
-template <typename C>
-struct IsBitsetImpl : std::false_type {};
-template <template <size_t> class B, size_t N>
-struct IsBitsetImpl<B<N>> : std::is_same<B<N>, std::bitset<N>> {};
-template <typename C>
-using IsBitset = IsBitsetImpl<std::decay_t<C>>;
+template <typename C, typename V, size_t N>
+IsCastableToDerivedSTLContainer<C, std::array<typename C::value_type, N>>
+CastableToDerivedSTLContainer(std::array<V, N>*);
 
-template <typename C>
-struct IsSTLContainer
-    : std::disjunction<
-          IsArray<C>, IsBitset<C>, IsSpecialization<C, std::deque>,
-          IsSpecialization<C, std::forward_list>,
-          IsSpecialization<C, std::list>, IsSpecialization<C, std::map>,
-          IsSpecialization<C, std::multimap>, IsSpecialization<C, std::set>,
-          IsSpecialization<C, std::multiset>,
-          IsSpecialization<C, std::unordered_map>,
-          IsSpecialization<C, std::unordered_multimap>,
-          IsSpecialization<C, std::unordered_set>,
-          IsSpecialization<C, std::unordered_multiset>,
-          IsSpecialization<C, std::vector>> {};
+template <typename C, size_t N>
+IsCastableToDerivedSTLContainer<C, std::bitset<N>>
+CastableToDerivedSTLContainer(std::bitset<N>*);
 
-template <typename C, template <typename...> class T, typename = void>
-struct IsBaseOfSpecializationImpl : std::false_type {};
-// IsBaseOfSpecializationImpl needs multiple partial specializations to SFINAE
-// on the existence of container dependent types and plug them into the STL
-// template.
-template <typename C, template <typename, typename> class T>
-struct IsBaseOfSpecializationImpl<
-    C, T, std::void_t<typename C::value_type, typename C::allocator_type>>
-    : std::is_base_of<C,
-                      T<typename C::value_type, typename C::allocator_type>> {};
-template <typename C, template <typename, typename, typename> class T>
-struct IsBaseOfSpecializationImpl<
-    C, T,
-    std::void_t<typename C::key_type, typename C::key_compare,
-                typename C::allocator_type>>
-    : std::is_base_of<C, T<typename C::key_type, typename C::key_compare,
-                           typename C::allocator_type>> {};
-template <typename C, template <typename, typename, typename, typename> class T>
-struct IsBaseOfSpecializationImpl<
-    C, T,
-    std::void_t<typename C::key_type, typename C::mapped_type,
+template <typename C, typename... U>
+IsCastableToDerivedSTLContainer<
+    C, std::deque<typename C::value_type, typename C::allocator_type>>
+CastableToDerivedSTLContainer(std::deque<U...>*);
+
+template <typename C, typename... U>
+IsCastableToDerivedSTLContainer<
+    C, std::forward_list<typename C::value_type, typename C::allocator_type>>
+CastableToDerivedSTLContainer(std::forward_list<U...>*);
+
+template <typename C, typename... U>
+IsCastableToDerivedSTLContainer<
+    C, std::list<typename C::value_type, typename C::allocator_type>>
+CastableToDerivedSTLContainer(std::list<U...>*);
+
+template <typename C, typename... U>
+IsCastableToDerivedSTLContainer<
+    C, std::map<typename C::key_type, typename C::mapped_type,
                 typename C::key_compare, typename C::allocator_type>>
-    : std::is_base_of<C,
-                      T<typename C::key_type, typename C::mapped_type,
-                        typename C::key_compare, typename C::allocator_type>> {
-};
-template <typename C, template <typename, typename, typename, typename> class T>
-struct IsBaseOfSpecializationImpl<
-    C, T,
-    // Roundabout equivalent of std::void_t that works around a template
-    // redefinition error from Clang.
-    std::conditional_t<
-        true, void,
-        std::common_type<typename C::key_type, typename C::hasher,
-                         typename C::key_equal, typename C::allocator_type>>>
-    : std::is_base_of<C, T<typename C::key_type, typename C::hasher,
-                           typename C::key_equal, typename C::allocator_type>> {
-};
-template <typename C,
-          template <typename, typename, typename, typename, typename> class T>
-struct IsBaseOfSpecializationImpl<
-    C, T,
-    std::void_t<typename C::key_type, typename C::mapped_type,
-                typename C::hasher, typename C::key_equal,
+CastableToDerivedSTLContainer(std::map<U...>*);
+
+template <typename C, typename... U>
+IsCastableToDerivedSTLContainer<
+    C, std::multimap<typename C::key_type, typename C::mapped_type,
+                     typename C::key_compare, typename C::allocator_type>>
+CastableToDerivedSTLContainer(std::multimap<U...>*);
+
+template <typename C, typename... U>
+IsCastableToDerivedSTLContainer<
+    C, std::set<typename C::value_type, typename C::key_compare,
                 typename C::allocator_type>>
-    : std::is_base_of<C, T<typename C::key_type, typename C::mapped_type,
-                           typename C::hasher, typename C::key_equal,
-                           typename C::allocator_type>> {};
-template <typename C, template <typename...> class T>
-using IsBaseOfSpecialization = IsBaseOfSpecializationImpl<std::decay_t<C>, T>;
+CastableToDerivedSTLContainer(std::set<U...>*);
 
-template <typename C>
-struct IsBaseOfArrayImpl : std::false_type {};
-template <template <typename, size_t> class A, typename T, size_t N>
-struct IsBaseOfArrayImpl<A<T, N>> : std::is_base_of<A<T, N>, std::array<T, N>> {
-};
-template <typename C>
-using IsBaseOfArray = IsBaseOfArrayImpl<std::decay_t<C>>;
+template <typename C, typename... U>
+IsCastableToDerivedSTLContainer<
+    C, std::multiset<typename C::value_type, typename C::key_compare,
+                     typename C::allocator_type>>
+CastableToDerivedSTLContainer(std::multiset<U...>*);
 
-template <typename C>
-struct IsBaseOfBitsetImpl : std::false_type {};
-template <template <size_t> class B, size_t N>
-struct IsBaseOfBitsetImpl<B<N>> : std::is_base_of<B<N>, std::bitset<N>> {};
-template <typename C>
-using IsBaseOfBitset = IsBaseOfBitsetImpl<std::decay_t<C>>;
+template <typename C, typename... U>
+IsCastableToDerivedSTLContainer<
+    C, std::unordered_map<typename C::key_type, typename C::mapped_type,
+                          typename C::hasher, typename C::key_equal,
+                          typename C::allocator_type>>
+CastableToDerivedSTLContainer(std::unordered_map<U...>*);
 
-template <typename C>
-struct IsBaseOfSTLContainer
-    : std::disjunction<IsBaseOfArray<C>, IsBaseOfBitset<C>,
-                       IsBaseOfSpecialization<C, std::deque>,
-                       IsBaseOfSpecialization<C, std::forward_list>,
-                       IsBaseOfSpecialization<C, std::list>,
-                       IsBaseOfSpecialization<C, std::map>,
-                       IsBaseOfSpecialization<C, std::multimap>,
-                       IsBaseOfSpecialization<C, std::set>,
-                       IsBaseOfSpecialization<C, std::multiset>,
-                       IsBaseOfSpecialization<C, std::unordered_map>,
-                       IsBaseOfSpecialization<C, std::unordered_multimap>,
-                       IsBaseOfSpecialization<C, std::unordered_set>,
-                       IsBaseOfSpecialization<C, std::unordered_multiset>,
-                       IsBaseOfSpecialization<C, std::vector>> {};
-
-template <typename C, template <typename...> class T, typename = void>
-struct IsConvertibleToSpecializationImpl : std::false_type {};
-// IsConvertibleToSpecializationImpl needs multiple partial specializations to
-// SFINAE on the existence of container dependent types and plug them into the
-// STL template.
-template <typename C, template <typename, typename> class T>
-struct IsConvertibleToSpecializationImpl<
-    C, T, std::void_t<typename C::value_type, typename C::allocator_type>>
-    : std::is_convertible<
-          C, T<typename C::value_type, typename C::allocator_type>> {};
-template <typename C, template <typename, typename, typename> class T>
-struct IsConvertibleToSpecializationImpl<
-    C, T,
-    std::void_t<typename C::key_type, typename C::key_compare,
-                typename C::allocator_type>>
-    : std::is_convertible<C, T<typename C::key_type, typename C::key_compare,
-                               typename C::allocator_type>> {};
-template <typename C, template <typename, typename, typename, typename> class T>
-struct IsConvertibleToSpecializationImpl<
-    C, T,
-    std::void_t<typename C::key_type, typename C::mapped_type,
-                typename C::key_compare, typename C::allocator_type>>
-    : std::is_convertible<
-          C, T<typename C::key_type, typename C::mapped_type,
-               typename C::key_compare, typename C::allocator_type>> {};
-template <typename C, template <typename, typename, typename, typename> class T>
-struct IsConvertibleToSpecializationImpl<
-    C, T,
-    // Roundabout equivalent of std::void_t that works around a template
-    // redefinition error from Clang.
-    std::conditional_t<
-        true, void,
-        std::common_type<typename C::key_type, typename C::hasher,
-                         typename C::key_equal, typename C::allocator_type>>>
-    : std::is_convertible<
-          C, T<typename C::key_type, typename C::hasher, typename C::key_equal,
-               typename C::allocator_type>> {};
-template <typename C,
-          template <typename, typename, typename, typename, typename> class T>
-struct IsConvertibleToSpecializationImpl<
-    C, T,
-    std::void_t<typename C::key_type, typename C::mapped_type,
-                typename C::hasher, typename C::key_equal,
-                typename C::allocator_type>>
-    : std::is_convertible<C, T<typename C::key_type, typename C::mapped_type,
+template <typename C, typename... U>
+IsCastableToDerivedSTLContainer<
+    C, std::unordered_multimap<typename C::key_type, typename C::mapped_type,
                                typename C::hasher, typename C::key_equal,
-                               typename C::allocator_type>> {};
-template <typename C, template <typename...> class T>
-using IsConvertibleToSpecialization =
-    IsConvertibleToSpecializationImpl<std::decay_t<C>, T>;
+                               typename C::allocator_type>>
+CastableToDerivedSTLContainer(std::unordered_multimap<U...>*);
 
-template <typename C>
-struct IsConvertibleToArrayImpl : std::false_type {};
-template <template <typename, size_t> class A, typename T, size_t N>
-struct IsConvertibleToArrayImpl<A<T, N>>
-    : std::is_convertible<A<T, N>, std::array<T, N>> {};
-template <typename C>
-using IsConvertibleToArray = IsConvertibleToArrayImpl<std::decay_t<C>>;
+template <typename C, typename... U>
+IsCastableToDerivedSTLContainer<
+    C, std::unordered_set<typename C::key_type, typename C::hasher,
+                          typename C::key_equal, typename C::allocator_type>>
+CastableToDerivedSTLContainer(std::unordered_set<U...>*);
 
-template <typename C>
-struct IsConvertibleToBitsetImpl : std::false_type {};
-template <template <size_t> class B, size_t N>
-struct IsConvertibleToBitsetImpl<B<N>>
-    : std::is_convertible<B<N>, std::bitset<N>> {};
-template <typename C>
-using IsConvertibleToBitset = IsConvertibleToBitsetImpl<std::decay_t<C>>;
+template <typename C, typename... U>
+IsCastableToDerivedSTLContainer<
+    C,
+    std::unordered_multiset<typename C::key_type, typename C::hasher,
+                            typename C::key_equal, typename C::allocator_type>>
+CastableToDerivedSTLContainer(std::unordered_multiset<U...>*);
 
-template <typename C>
-struct IsConvertibleToSTLContainer
-    : std::disjunction<
-          IsConvertibleToArray<C>, IsConvertibleToBitset<C>,
-          IsConvertibleToSpecialization<C, std::deque>,
-          IsConvertibleToSpecialization<C, std::forward_list>,
-          IsConvertibleToSpecialization<C, std::list>,
-          IsConvertibleToSpecialization<C, std::map>,
-          IsConvertibleToSpecialization<C, std::multimap>,
-          IsConvertibleToSpecialization<C, std::set>,
-          IsConvertibleToSpecialization<C, std::multiset>,
-          IsConvertibleToSpecialization<C, std::unordered_map>,
-          IsConvertibleToSpecialization<C, std::unordered_multimap>,
-          IsConvertibleToSpecialization<C, std::unordered_set>,
-          IsConvertibleToSpecialization<C, std::unordered_multiset>,
-          IsConvertibleToSpecialization<C, std::vector>> {};
+template <typename C, typename... U>
+IsCastableToDerivedSTLContainer<
+    C, std::vector<typename C::value_type, typename C::allocator_type>>
+CastableToDerivedSTLContainer(std::vector<U...>*);
 
 template <typename C>
 struct IsStrictlyBaseOfAndConvertibleToSTLContainer
-    : std::conjunction<std::negation<IsSTLContainer<C>>,
-                       IsBaseOfSTLContainer<C>,
-                       IsConvertibleToSTLContainer<C>> {};
+    : decltype(strings_internal::CastableToDerivedSTLContainer<
+               absl::remove_cvref_t<C>>(
+          std::declval<absl::remove_cvref_t<C>*>())) {};
 
 }  // namespace strings_internal
 ABSL_NAMESPACE_END