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) const for any lookup type U you intend to support.
  • Equality functor: Must provide bool operator()(U lhs, V rhs) const that returns true when 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_map accepts custom Hash and Eq types 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 in flat_hash_map.h lines 95–100.
  • Hash functors must implement size_t operator()(U val) const for supported lookup types.
  • Equality functors must implement bool operator()(U lhs, V rhs) const for all comparison combinations.
  • Embedded functors named absl_container_hash and absl_container_eq enable automatic detection via the HashEq<T> machinery in hash_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:

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 →