Canonicalize pointer keys in SwissTable FindLargeKey. For pointer-keyed SwissTable containers using the default `HashEq<T*>::Hash` and `HashEq<T*>::Eq`, convert lookup keys implicitly convertible to `const T*` to `const T*` in `FindLargeKey<K>`. This shares `find_large` and `find_or_prepare_insert_large` instantiations across `T*`, `const T*`, `void*`, and derived-pointer lookups on the same container. PiperOrigin-RevId: 1005017417
diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h index b7fb4d1..5d1115a 100644 --- a/absl/container/internal/raw_hash_set.h +++ b/absl/container/internal/raw_hash_set.h
@@ -4018,14 +4018,26 @@ private: friend struct RawHashSetTestOnlyAccess; + template <class K, class KeyType = key_type> + struct IsConvertibleToKeyPtr : std::false_type {}; + + template <class K, class T> + struct IsConvertibleToKeyPtr<K, T*> + : std::bool_constant< + std::is_same_v<hasher, typename HashEq<T*>::Hash> && + std::is_same_v<key_equal, typename HashEq<T*>::Eq> && + std::is_convertible_v<const absl::remove_cvref_t<K>&, const T*>> {}; + // For string-like keys, we convert to string_view when calling find*_large() // operations to (a) avoid repeatedly (i) branching on SSO or (ii) calling - // strlen(), (b) share code between the string-like types. Note that we don't - // pass FindLargeKey to find*_small() to avoid unnecessarily constructing - // string_view for small empty tables. Note: we use type_identity_t and - // static_asserts to ensure that the key type in find*_large is FindLargeKey. - // (type_identity_t ensures that the template argument is not deduced and must - // be specified explicitly.) + // strlen(), (b) share code between the string-like types. For pointer keys, + // we similarly convert to `const T*` to share code between `T*`, `const T*`, + // and derived-pointer lookups. Note that we don't pass FindLargeKey to + // find*_small() to avoid unnecessarily constructing string_view for small + // empty tables. Note: we use type_identity_t and static_asserts to ensure + // that the key type in find*_large is FindLargeKey. (type_identity_t ensures + // that the template argument is not deduced and must be specified + // explicitly.) template <class K> using FindLargeKey = type_traits_internal::AddConstRefIfExpensiveToCopy<std::conditional_t< @@ -4033,7 +4045,10 @@ std::is_same_v<key_equal, absl::container_internal::StringEq> && std::is_convertible_v<const absl::remove_cvref_t<K>&, absl::string_view>, - absl::string_view, absl::remove_cvref_t<K>>>; + absl::string_view, + std::conditional_t<IsConvertibleToKeyPtr<K>::value, + const std::remove_pointer_t<key_type>*, + absl::remove_cvref_t<K>>>>; // Prefetch the heap-allocated memory region to resolve potential TLB and // cache misses. This is intended to overlap with execution of calculating the
diff --git a/absl/container/internal/raw_hash_set_test.cc b/absl/container/internal/raw_hash_set_test.cc index 60fb142..a51ab9b 100644 --- a/absl/container/internal/raw_hash_set_test.cc +++ b/absl/container/internal/raw_hash_set_test.cc
@@ -91,6 +91,8 @@ static size_t CountTombstones(const C& c) { return c.common().TombstonesCount(); } + template <typename C, typename K> + using FindLargeKey = typename C::template FindLargeKey<K>; }; namespace { @@ -5485,6 +5487,31 @@ EXPECT_EQ(t.capacity(), kTargetCapacity); } +TEST(Table, FindLargeKeyPointerCanonicalization) { + struct Base {}; + struct Derived : Base {}; + + using Set = absl::flat_hash_set<int*>; + static_assert( + std::is_same_v<RawHashSetTestOnlyAccess::FindLargeKey<Set, int*>, + const int*>); + static_assert( + std::is_same_v< + RawHashSetTestOnlyAccess::FindLargeKey<Set, std::unique_ptr<int>>, + const std::unique_ptr<int>&>); + + using BaseSet = absl::flat_hash_set<const Base*>; + static_assert( + std::is_same_v<RawHashSetTestOnlyAccess::FindLargeKey<BaseSet, Derived*>, + const Base*>); + + using CustomSet = + absl::flat_hash_set<int*, absl::Hash<int*>, std::equal_to<int*>>; + static_assert( + std::is_same_v<RawHashSetTestOnlyAccess::FindLargeKey<CustomSet, int*>, + int*>); +} + } // namespace } // namespace container_internal ABSL_NAMESPACE_END