absl::flat_hash_map Heterogeneous Lookup: Requirements and Implementation
absl::flat_hash_map enables heterogeneous lookup by detecting the is_transparent nested type in Hash and Eq functors, allowing direct key comparison without temporary object construction.
The absl::flat_hash_map container in the abseil/abseil-cpp repository provides high-performance hash map operations with zero-allocation heterogeneous lookup capabilities. Unlike standard associative containers that require converting lookup arguments to the map's key type, this implementation forwards heterogeneous keys directly to transparent hashing and equality functors. Understanding these mechanics allows developers to implement efficient custom key types that avoid unnecessary memory allocations during lookup operations.
How absl::flat_hash_map Implements Heterogeneous Lookup
The container delegates hash computation and equality checks to the Hash and Eq template parameters provided during instantiation. When invoking lookup functions such as find(), operator[](), or insert() with a key type differing from the map's native key type K, the implementation checks whether both functors expose a nested type named is_transparent.
According to the source documentation in /absl/container/flat_hash_map.h (lines 64-69), heterogeneous lookup is activated only when both functors define this marker type. When present, the container forwards the supplied lookup key directly to Hash::operator() and Eq::operator() without constructing a temporary K object. The precise requirements are detailed in lines 95-104 of the same header, which specify that functors must accept heterogeneous types in their call operators to support this behavior.
Requirements for Custom Types
To enable heterogeneous lookup for custom key types, you must satisfy one of two architectural approaches. Both require the hash and equality functors to be callable with the heterogeneous key type passed to lookup functions.
Providing Custom Hash and Equality Functors
Define explicit functors with the following characteristics:
Hashfunctor: Must definesize_t operator()(U const& val) constfor every lookup typeUyou intend to supportEqfunctor: Must definebool operator()(U const& lhs, V const& rhs) const(and symmetric overloads) for the same type combinations- Transparency marker: Both functors must contain
using is_transparent = void;(or any type) to signal heterogeneous lookup capability
The container does not automatically convert lookup keys to type K; the functors must handle conversion or direct comparison logic internally.
Using Inner Types on the Key Structure
Alternatively, embed the functors within the key type itself:
- Define
using absl_container_hash = <your_hash_functor>;inside the key type - Optionally define
using absl_container_eq = <your_eq_functor>;(defaults tostd::equal_to<void>if omitted) - Ensure the inner hash functor exposes
is_transparentand handles heterogeneous inputs
Practical Implementation Examples
Case-Insensitive Lookup with Custom Functors
This example demonstrates transparent functors that enable lookup using const char* and std::string_view without allocating temporary std::string objects:
#include "absl/container/flat_hash_map.h"
#include <string>
#include <string_view>
struct CaseInsensitiveHash {
using is_transparent = void; // Marks functor as transparent
size_t operator()(std::string_view s) const {
size_t h = 0;
for (char c : s) h = h * 31 + std::tolower(c);
return h;
}
};
struct CaseInsensitiveEq {
using is_transparent = void;
bool operator()(std::string_view a, std::string_view b) const {
return std::equal_to<>{}(std::tolower_view(a), std::tolower_view(b));
}
};
int main() {
absl::flat_hash_map<std::string, int, CaseInsensitiveHash, CaseInsensitiveEq> m;
m["Apple"] = 1;
// Heterogeneous lookup using const char* - no string allocation
if (auto it = m.find("apple"); it != m.end()) {
std::cout << it->second << '\n'; // Prints 1
}
}
Transparent Hash via Inner Types
Define hash functionality within the key structure to support partial key lookups:
#include "absl/container/flat_hash_map.h"
#include <string>
#include <string_view>
struct MyKey {
std::string name;
int id;
struct Hash {
using is_transparent = void;
size_t operator()(std::string_view sv) const {
return std::hash<std::string_view>{}(sv);
}
size_t operator()(MyKey const& k) const {
return std::hash<std::string_view>{}(k.name);
}
};
using absl_container_hash = Hash; // Signals flat_hash_map to use inner functor
};
int main() {
absl::flat_hash_map<MyKey, int> map;
map[{ "Bob", 42 }] = 100;
// Search using string_view only - no MyKey construction required
if (auto it = map.find("Bob"); it != map.end()) {
std::cout << it->second << '\n'; // Prints 100
}
}
Key Source Files
Understanding the implementation requires examining these specific files in the abseil/abseil-cpp repository:
absl/container/flat_hash_map.h– Primary container definition documenting theis_transparentpattern and heterogeneous lookup requirements (lines 64-69 and 95-104)absl/container/internal/raw_hash_map.h– Low-level hash table implementation used byflat_hash_mapabsl/container/internal/hash_function_defaults.h– Defines default hash functors providingis_transparentsupportabsl/container/internal/heterogeneous_lookup_testing.h– Unit test utilities demonstrating transparent functor patterns
Summary
- Transparency detection: The container checks for
is_transparentnested types in bothHashandEqfunctors to enable heterogeneous lookup - Zero-allocation lookups: Heterogeneous keys forward directly to functors without constructing temporary native key objects
- Dual implementation paths: Support custom functors passed as template parameters or inner types (
absl_container_hash,absl_container_eq) defined within the key class - Symmetric requirements: Both hashing and equality functors must expose
is_transparentand handle the heterogeneous types used in lookups - Performance optimization: This mechanism eliminates string allocations and copy operations when lookup keys differ from stored key types
Frequently Asked Questions
What happens if only one functor has is_transparent?
Heterogeneous lookup requires both the Hash and Eq functors to define the is_transparent nested type. If only one functor exposes this marker, the container falls back to standard behavior and constructs a temporary key object of type K before performing the lookup operation.
Can I use the default std::hash with heterogeneous lookup?
No, standard library hash functors like std::hash do not define is_transparent and only accept the exact key type. To enable heterogeneous lookup, you must provide custom functors that accept the alternative lookup types (such as std::string_view) and expose the transparency marker. The default functors in absl/container/internal/hash_function_defaults.h provide reference implementations for common string types.
Does heterogeneous lookup work with absl::flat_hash_set?
Yes, the heterogeneous lookup mechanism described in /absl/container/flat_hash_map.h applies to all Abseil hash-based containers, including absl::flat_hash_set, absl::node_hash_map, and absl::node_hash_set. They share the same underlying implementation in raw_hash_map.h and require the same is_transparent pattern in their hash and equality functors.
Why does my custom type require absl_container_hash instead of std::hash specialization?
While you can specialize std::hash, Abseil containers check for the inner type absl_container_hash as a mechanism to automatically select appropriate functors without requiring template parameters at the container declaration. This approach keeps the container declaration clean (absl::flat_hash_map<MyKey, int>) while still enabling heterogeneous lookup through the embedded transparent functor.
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 →