How to Implement Custom Hash Functions and Equality Predicates for absl::flat_hash_map
To implement custom hash functions and equality predicates for absl::flat_hash_map, supply your own Hash and Eq types as the third and fourth template arguments, ensuring they expose is_transparent for heterogeneous lookup and implement the required operator overloads.
The absl::flat_hash_map container in the Abseil C++ library provides high-performance associative array operations backed by the SwissTable algorithm. While it defaults to the Abseil hashing framework (absl::Hash), the template parameters allow you to inject custom behavior for specialized key types or heterogeneous lookup requirements. Understanding the specific requirements documented in the Abseil source code ensures your custom functors integrate seamlessly with the container's internal machinery.
Understanding flat_hash_map Template Parameters
The absl::flat_hash_map class template accepts four parameters, with the last two controlling hashing and equality:
template <class K, class V,
class Hash = DefaultHash,
class Eq = DefaultEq>
class flat_hash_map { … };
The default hash resolves to absl::container_internal::hash_default_hash<K>, and the default equality resolves to absl::container_internal::hash_default_eq<K>. These defaults are defined in absl/container/hash_container_defaults.h and ultimately implemented in absl/container/internal/hash_function_defaults.h. If your key type defines inner types named absl_container_hash and absl_container_eq, the library automatically detects them via the HashEq<T> specialization located at lines 334–344 in hash_function_defaults.h.
Requirements for Custom Hash and Equality Functors
Custom functors must satisfy two specific conditions to work correctly with absl::flat_hash_map as documented in absl/container/flat_hash_map.h (lines 95–104).
Enabling Heterogeneous Lookup with is_transparent
To support heterogeneous lookup (e.g., finding a std::string key using absl::string_view), your custom functors must define a nested type:
using is_transparent = void;
This marker type signals the container to enable overloads that accept lookup keys differing from the stored key type. Without this definition, the container restricts lookups to the exact key type, degrading flexibility.
Required Callable Signatures
Your custom functors must implement specific operator overloads:
- Hash functor: Must provide
size_t operator()(U val) constfor any lookup typeUyou intend to support. - Equality functor: Must provide
bool operator()(U lhs, V rhs) constthat returnstruewhen the arguments compare equal. You typically need three overloads:(Key, Key),(Key, Lookup), and(Lookup, Key)to handle heterogeneous comparisons bidirectionally.
Implementation Examples
The following examples demonstrate different strategies for providing custom hash and equality logic to absl::flat_hash_map.
Example 1: Custom Functors for User-Defined Structs
For a custom Point struct, define separate hash and equality functors that support heterogeneous lookup with std::pair<int,int>:
struct Point {
int x, y;
};
struct PointHash {
using is_transparent = void;
std::size_t operator()(const Point& p) const {
return (static_cast<std::size_t>(p.x) << 32) ^ static_cast<std::size_t>(p.y);
}
std::size_t operator()(const std::pair<int,int>& pr) const {
return (static_cast<std::size_t>(pr.first) << 32) ^ static_cast<std::size_t>(pr.second);
}
};
struct PointEq {
using is_transparent = void;
bool operator()(const Point& a, const Point& b) const {
return a.x == b.x && a.y == b.y;
}
bool operator()(const Point& a, const std::pair<int,int>& pr) const {
return a.x == pr.first && a.y == pr.second;
}
bool operator()(const std::pair<int,int>& pr, const Point& a) const {
return operator()(a, pr);
}
};
// Usage
absl::flat_hash_map<Point, std::string, PointHash, PointEq> point_map;
point_map.emplace(Point{1, 2}, "A");
// Heterogeneous lookup with pair<int,int>
auto it = point_map.find(std::make_pair(1, 2));
Example 2: Embedded Hash and Equality Types
You can embed the functors directly within your key type using the special names absl_container_hash and absl_container_eq. This allows absl::flat_hash_map to automatically deduce the custom functors without explicit template arguments:
struct UserId {
std::string id;
struct absl_container_hash {
using is_transparent = void;
std::size_t operator()(absl::string_view v) const {
return absl::Hash<absl::string_view>{}(v);
}
std::size_t operator()(const UserId& u) const {
return absl::Hash<std::string>{}(u.id);
}
};
struct absl_container_eq {
using is_transparent = void;
bool operator()(absl::string_view lhs, absl::string_view rhs) const {
return lhs == rhs;
}
bool operator()(const UserId& lhs, const UserId& rhs) const {
return lhs.id == rhs.id;
}
bool operator()(absl::string_view lhs, const UserId& rhs) const {
return lhs == rhs.id;
}
bool operator()(const UserId& lhs, absl::string_view rhs) const {
return lhs.id == rhs;
}
};
};
// Template arguments deduced automatically
absl::flat_hash_map<UserId, int> id_to_score;
id_to_score.emplace(UserId{"alice"}, 42);
// Lookup with string_view - no temporary construction needed
auto it = id_to_score.find(absl::string_view("alice"));
Example 3: Using Lambdas with C++20
For quick one-off customization, you can use lambdas (C++20) by wrapping them in the template declaration:
auto lambda_hash = [](const std::string& s) -> std::size_t {
return absl::Hash<std::string>{}(s);
};
struct LambdaEq {
using is_transparent = void;
bool operator()(const std::string& a, const std::string& b) const {
return a == b;
}
};
absl::flat_hash_map<std::string, double,
decltype(lambda_hash), LambdaEq> price_map(0, lambda_hash);
price_map["apple"] = 1.23;
Summary
absl::flat_hash_mapaccepts customHashandEqtypes as the third and fourth template parameters, defaulting to Abseil's internal hash defaults.- Heterogeneous lookup requires your functors to define
using is_transparent = void;as documented inflat_hash_map.hlines 95–100. - Hash functors must implement
size_t operator()(U val) constfor supported lookup types. - Equality functors must implement
bool operator()(U lhs, V rhs) constfor all comparison combinations. - Embedded functors named
absl_container_hashandabsl_container_eqenable automatic detection via theHashEq<T>machinery inhash_function_defaults.h.
Frequently Asked Questions
What is the default hash function used by absl::flat_hash_map?
By default, absl::flat_hash_map uses absl::container_internal::hash_default_hash<K> as defined in absl/container/internal/hash_function_defaults.h. This default integrates with the Abseil hashing framework and supports the standard hashing protocol for built-in and common types.
Why do I need to define is_transparent in my custom functors?
The nested type is_transparent enables heterogeneous lookup, allowing you to search for keys using types different from the stored key type (e.g., looking up a std::string key with absl::string_view). Without this definition, the container restricts lookups to exact key matches, preventing optimizations that avoid unnecessary conversions.
Can I use lambdas for custom hash functions in absl::flat_hash_map?
Yes, you can use lambdas for custom hash functions, particularly with C++20. You must declare the lambda (or wrap it in a struct) and pass its type via decltype to the template parameters, then provide the lambda instance as a constructor argument to initialize the map's hasher.
How do I enable automatic hash detection for my custom type?
Define inner types named absl_container_hash and optionally absl_container_eq within your key class. According to the HashEq<T> specialization in absl/container/internal/hash_function_defaults.h (lines 334–344), the library automatically detects these types and uses them as the hash and equality functors when you declare absl::flat_hash_map<MyKey, Value> without explicit template arguments.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →