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:

  • Hash functor: Must define size_t operator()(U const& val) const for every lookup type U you intend to support
  • Eq functor: Must define bool 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 to std::equal_to<void> if omitted)
  • Ensure the inner hash functor exposes is_transparent and 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:

Summary

  • Transparency detection: The container checks for is_transparent nested types in both Hash and Eq functors 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_transparent and 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →