# How to Integrate Custom Types with absl::Hash for Swiss Tables in Abseil

> Learn how to integrate custom types with absl Hash for Abseil Swiss tables. Provide an AbslHashValue overload in your type's namespace for seamless container integration.

- Repository: [Abseil/abseil-cpp](https://github.com/abseil/abseil-cpp)
- Tags: how-to-guide
- Published: 2026-07-18

---

**To make a custom type hashable for Abseil's Swiss-table containers, provide an `AbslHashValue` overload in the same namespace as your type that combines the object's fields into the provided hash state, which the container automatically discovers via argument-dependent lookup.**

Abseil's hashing framework decouples the hash algorithm from the hash state, enabling efficient "Swiss-table" containers like `absl::flat_hash_set` and `absl::node_hash_map`. Integrating custom types requires implementing the `AbslHashValue` extension point defined in [`absl/hash/hash.h`](https://github.com/abseil/abseil-cpp/blob/main/absl/hash/hash.h). This article demonstrates the exact implementation patterns used in the `abseil/abseil-cpp` repository.

## The absl::Hash Lookup Order

When a Swiss-table container needs to hash a value, it invokes `absl::Hash<T>`, which follows a strict resolution order defined in [`absl/hash/hash.h`](https://github.com/abseil/abseil-cpp/blob/main/absl/hash/hash.h) (lines 155–162). The framework checks three possible sources:

1. **Built-in support** — Primitives, strings, pointers, and other fundamental types handled internally.
2. **User-provided `AbslHashValue`** — A free function discovered via argument-dependent lookup (ADL) in the type's namespace.
3. **`std::hash<T>` specialization** — A backward-compatibility fallback maintained for legacy code.

The Swiss-table containers use the default hash policy defined in [`absl/container/internal/hash_function_defaults.h`](https://github.com/abseil/abseil-cpp/blob/main/absl/container/internal/hash_function_defaults.h) (lines 15–37), which forwards directly to `absl::Hash`. This means any type satisfying step two above works automatically without specializing the container's template arguments.

## Implementing the AbslHashValue Extension Point

The `AbslHashValue` function receives a *hash state* (`H`) and must return the state after combining the type's members. The state's `combine` method—implemented in [`absl/hash/internal/hash.h`](https://github.com/abseil/abseil-cpp/blob/main/absl/hash/internal/hash.h)—mixes each field using the currently selected hash algorithm (chosen per-process at startup).

### Public Struct with Public Members

For simple aggregates, declare the overload as a `friend` function inside the struct definition. This places the function in the surrounding namespace while granting it access to members.

```cpp
// point.h
#include "absl/hash/hash.h"

struct Point {
  int x;
  int y;

  template <typename H>
  friend H AbslHashValue(H state, const Point& p) {
    return H::combine(std::move(state), p.x, p.y);
  }
};

```

```cpp
// main.cc
#include "absl/container/flat_hash_set.h"
#include "point.h"

int main() {
  absl::flat_hash_set<Point> points;
  points.insert({1, 2});
  
  // Lookups automatically use the custom hash
  if (points.contains(Point{1, 2})) { /* ... */ }
}

```

### Private Members and Friend Declarations

When members are private, the `friend` declaration remains the idiomatic pattern. The function is defined inline within the class, making it a non-member function found via ADL while accessing private data.

```cpp
// secret_id.h
#include "absl/hash/hash.h"

class SecretId {
 public:
  explicit SecretId(uint64_t v) : value_(v) {}

 private:
  uint64_t value_;

  template <typename H>
  friend H AbslHashValue(H state, const SecretId& id) {
    return H::combine(std::move(state), id.value_);
  }
};

```

```cpp
// main.cc
#include "absl/container/node_hash_map.h"
#include "secret_id.h"

int main() {
  absl::node_hash_map<SecretId, std::string> map;
  map.emplace(SecretId{42}, "answer");
}

```

## Enabling Heterogeneous Lookup

To avoid constructing temporary objects during lookups, provide an additional `AbslHashValue` overload that accepts a compatible "view" type. This allows the Swiss table to hash a `absl::string_view` when the key type is a custom string wrapper, for example.

```cpp
// name.h
#include "absl/hash/hash.h"
#include <string>

class Name {
 public:
  explicit Name(std::string s) : data_(std::move(s)) {}
  const std::string& get() const { return data_; }

 private:
  std::string data_;

  template <typename H>
  friend H AbslHashValue(H state, const Name& n) {
    return H::combine(std::move(state), n.data_);
  }
};

// Heterogeneous overload for string_view
template <typename H>
inline H AbslHashValue(H state, absl::string_view sv) {
  return H::combine(std::move(state), sv);
}

```

```cpp
// main.cc
#include "absl/container/flat_hash_set.h"
#include "name.h"

int main() {
  absl::flat_hash_set<Name> names;
  names.emplace(Name{"Alice"});
  
  // Lookup without constructing a Name object
  if (names.contains(absl::string_view("Alice"))) { /* ... */ }
}

```

## How Swiss Tables Consume Hash Values

The core Swiss-table implementation in [`absl/container/internal/raw_hash_set.h`](https://github.com/abseil/abseil-cpp/blob/main/absl/container/internal/raw_hash_set.h) delegates all hashing operations to the hash functor provided by the container's policy. By default, `absl::flat_hash_set`, `absl::node_hash_map`, and related containers use the `Hash` alias defined in [`hash_function_defaults.h`](https://github.com/abseil/abseil-cpp/blob/main/hash_function_defaults.h), which is `absl::Hash<T>`.

Because `absl::Hash` resolves to user-defined `AbslHashValue` overloads via ADL, no additional configuration is required. Once the overload is visible at the point of container instantiation, insertions, lookups, and rehashing automatically utilize the custom hash logic.

## Summary

- **Provide `AbslHashValue`** as a free function in your type's namespace (often as a `friend` inside the class) to make it hashable.
- **Combine fields** using `H::combine(std::move(state), member1, member2, ...)` to mix data into the hash state using the process-wide algorithm.
- **Support heterogeneous lookup** by overloading `AbslHashValue` for view types compatible with your class, avoiding temporary constructions during find operations.
- **No container specialization** is needed; `absl::flat_hash_set` and other Swiss tables automatically use `absl::Hash`, which discovers your overload via argument-dependent lookup.

## Frequently Asked Questions

### Why does Abseil use ADL to find AbslHashValue instead of requiring std::hash specialization?

Requiring `std::hash` specialization forces users to open the `std` namespace and cannot access private class members without exposing them or using hacky workarounds. The ADL-based `AbslHashValue` mechanism allows the hash logic to reside naturally in the type's namespace and leverages `friend` declarations for clean encapsulation of private data.

### What header files must I include to implement AbslHashValue?

Include `"absl/hash/hash.h"` to access the `AbslHashValue` extension point and the hash state interface. When using the type in containers, include the specific container header (e.g., `"absl/container/flat_hash_set.h"`), which transitively includes the default hash policies that forward to `absl::Hash`.

### How does the combine method handle different hash algorithms?

The `combine` method defined in [`absl/hash/internal/hash.h`](https://github.com/abseil/abseil-cpp/blob/main/absl/hash/internal/hash.h) type-erases the specific algorithm being used. When your `AbslHashValue` overload calls `H::combine`, it mixes the provided fields into the state using whichever algorithm was selected at process startup (e.g., Wyhash), ensuring consistent behavior across all types without hard-coding algorithm details.

### Can I use AbslHashValue with existing types I don't control?

No. `AbslHashValue` overloads must be defined in the same namespace as the type they hash (or as friends within the class). For types from external libraries where you cannot modify the namespace, specialize `absl::Hash<T>` directly, though this is discouraged in favor of wrapping the type in a custom struct that defines `AbslHashValue`.