# How absl::Hash Works with Custom Types: Requirements and Implementation Guide

> Learn how absl Hash works with custom types. Implement AbslHashValue by combining member fields with H::combine and return the hash state for your custom types.

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

---

**To make a custom type hashable with absl::Hash, provide a free function template named `AbslHashValue` in the same namespace as your type that combines member fields using `H::combine` and returns the updated hash state.**

The `absl::Hash` framework in the [abseil/abseil-cpp](https://github.com/abseil/abseil-cpp) repository provides a generic hashing mechanism that supports custom types through a specific extension interface. Unlike standard library hash specializations, Abseil's approach uses Argument-Dependent Lookup (ADL) and free functions to keep hashing logic separate from your type's public interface. Understanding how `absl::Hash` works with custom types ensures your classes integrate seamlessly with Abseil containers and hashing algorithms.

## The Three Conditions for Hashability

`absl::Hash<T>` can hash any type that satisfies one of three conditions, evaluated in order as defined in [`absl/hash/hash.h`](https://github.com/abseil/abseil-cpp/blob/main/absl/hash/hash.h) (lines 99-106):

1. **Arithmetic or pointer types**: Built-in integers, enums, floating-point numbers, and raw pointers are supported natively.
2. **`AbslHashValue` overload**: A free function template that combines the object's fields into a hash state.
3. **`std::hash<T>` specialization**: Legacy fallback support for types already compatible with the standard library.

The preferred approach is providing an `AbslHashValue` overload, as it leverages the full Abseil hashing framework and maintains better encapsulation.

## Requirements for Custom Types

### Define AbslHashValue as a Free Function

The `AbslHashValue` function must be a non-member function template defined in the same namespace as your custom type. According to the implementation in [`absl/hash/hash.h`](https://github.com/abseil/abseil-cpp/blob/main/absl/hash/hash.h), the function signature follows this pattern:

```cpp
template <typename H>
H AbslHashValue(H state, const MyType& v) {
    return H::combine(std::move(state), v.field1, v.field2);
}

```

Key requirements:

- The function **must return** the updated hash state of type `H`.
- It must be discoverable via **Argument-Dependent Lookup (ADL)**, requiring placement in the same namespace as the type.
- The function should combine all fields used in `operator==` to ensure that equal objects produce equal hash values.

### Ensure All Members Are Hashable

Every field passed to `H::combine` must itself be hashable by `absl::Hash`. The framework validates this at compile time using the `hash_internal::is_hashable<T>` trait (defined in [`absl/hash/internal/hash.h`](https://github.com/abseil/abseil-cpp/blob/main/absl/hash/internal/hash.h) around lines 9-12). If any member lacks hash support, compilation will fail with a diagnostic indicating the unhashable type.

### Avoid Specializing absl::Hash

The framework explicitly forbids specializing `absl::Hash` itself. As noted in [`absl/hash/hash.h`](https://github.com/abseil/abseil-cpp/blob/main/absl/hash/hash.h) (lines 52-53), all extensions must occur through `AbslHashValue` overloads rather than template specializations, ensuring consistent behavior across the hashing ecosystem.

## Implementation Examples

### Simple Struct with Public Members

For a basic struct, define the overload in the same namespace:

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

struct Point {
    int x;
    int y;
};

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

// Usage
absl::Hash<Point> hasher;
size_t h = hasher(Point{3, 7});

```

### Class with Private Members

When fields are private, declare `AbslHashValue` as a friend function:

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

class Circle {
    std::pair<int, int> center_;
    int radius_;

public:
    template <typename H>
    friend H AbslHashValue(H state, const Circle& c) {
        return H::combine(std::move(state), c.center_, c.radius_);
    }
};

```

### Type Erasure for PImpl Idioms

For types using the Pointer to Implementation (PImpl) pattern or virtual functions, use `absl::HashState` to erase the concrete hash state type:

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

class WidgetImpl;

class Widget {
public:
    void HashValue(absl::HashState state) const;

    template <typename H>
    friend H AbslHashValue(H state, const Widget& w) {
        state = H::combine(std::move(state), std::type_index(typeid(w)));
        w.HashValue(absl::HashState::Create(&state));
        return state;
    }
};

```

This approach, documented in [`absl/hash/hash.h`](https://github.com/abseil/abseil-cpp/blob/main/absl/hash/hash.h) (lines 78-86), adds overhead and should only be used when templates cannot be employed.

### Hashing Containers of Custom Types

Once `AbslHashValue` is defined, standard containers work automatically:

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

struct Item { 
    int id; 
    std::string name; 
};

template <typename H>
H AbslHashValue(H state, const Item& i) {
    return H::combine(std::move(state), i.id, i.name);
}

// Vector hashing uses the library's built-in container support
absl::Hash<std::vector<Item>> vec_hasher;
std::vector<Item> items = {{1, "a"}, {2, "b"}};
size_t hv = vec_hasher(items);

```

## Core Hash State Operations

The hash state `H` passed to `AbslHashValue` provides three primary combination methods implemented in `hash_internal::HashStateBase` (from [`absl/hash/internal/hash.h`](https://github.com/abseil/abseil-cpp/blob/main/absl/hash/internal/hash.h)):

- **`H::combine(state, v1, v2, ...)`**: Combines arbitrary variadic values into the state.
- **`H::combine_contiguous(state, data, size)`**: Mixes a contiguous byte range efficiently.
- **`H::combine_unordered(state, begin, end)`**: Provides order-independent hashing for unordered containers.

These primitives ensure high-quality hash distribution while maintaining consistency across the Abseil ecosystem.

## Summary

- **Provide `AbslHashValue`**: Define a free function template in your type's namespace that takes `H state` and `const T&`, returning `H::combine(std::move(state), members...)`.
- **Ensure ADL visibility**: Place the overload in the same namespace as your custom type so Argument-Dependent Lookup can find it.
- **Hash all equality fields**: Combine every member used in `operator==` to maintain the contract that equal objects have equal hashes.
- **Members must be hashable**: All fields passed to `H::combine` must themselves satisfy `absl::Hash` requirements.
- **No specialization**: Never specialize `absl::Hash<T>` directly; use `AbslHashValue` instead.
- **Type erasure option**: Use `absl::HashState::Create()` when templates are unavailable, such as in virtual functions or PImpl implementations.

## Frequently Asked Questions

### What happens if I forget to put AbslHashValue in the same namespace as my type?

If `AbslHashValue` is not defined in the same namespace as your custom type, Argument-Dependent Lookup (ADL) will fail to find the function. According to the logic in [`absl/hash/hash.h`](https://github.com/abseil/abseil-cpp/blob/main/absl/hash/hash.h) (lines 99-106), the framework will then attempt to fall back to `std::hash<T>`. If no `std::hash` specialization exists, you will receive a compilation error indicating that the type is not hashable.

### Can I specialize std::hash for my type instead of using AbslHashValue?

Yes, `absl::Hash` will detect and use a `std::hash<T>` specialization as a fallback mechanism. However, this is considered legacy support and is not recommended. Using `AbslHashValue` provides better integration with Abseil's hashing framework, supports more efficient state combination operations, and keeps hashing logic separate from your type's interface.

### How do I hash a custom container that stores elements in an arbitrary order?

For containers where element order does not affect equality (such as `std::unordered_set`), use `H::combine_unordered` instead of `H::combine`. This method, available in the hash state object, ensures that the same elements produce the same hash value regardless of their storage order. You can find the implementation details in [`absl/hash/internal/hash.h`](https://github.com/abseil/abseil-cpp/blob/main/absl/hash/internal/hash.h).

### Why does AbslHashValue need to be a free function rather than a member function?

`absl::Hash` uses Argument-Dependent Lookup (ADL) to find hashing implementations, which requires free functions defined in the same namespace as the type. Member functions cannot be found through ADL, and requiring them would force intrusive modifications to class interfaces. The free function approach allows you to add hashing support for types without modifying the class definition itself, such as when using the `friend` keyword for private members.