Files
json/docs/mkdocs/docs/api/basic_json/std_hash.md
T
Niels LohmannandAfonso Januário a5f5d3059b Make std::hash<basic_json> consistent with operator== for numbers (#5772)
* Make std::hash<basic_json> consistent with operator== for numbers

operator== converts between number_integer, number_unsigned, and
number_float before comparing, so json(0), json(0U), and json(0.0)
all compare equal. hash() folded the specific value_t into the
result for each of the three numeric cases, giving each a distinct
hash and breaking the standard Hash requirement that a == b implies
hash(a) == hash(b). A std::unordered_set could therefore hold all
three as separate elements even though they compare equal.

hash() now treats all three numeric variants the same way: it
converts the value to number_float_t and combines it with a single
shared type tag, so any two numbers operator== considers equal hash
identically regardless of which internal type actually holds them.

Updated the accompanying test to check this consistency directly
(including via an actual unordered_set) instead of asserting that 0,
0U, and 0.0 hash differently, since that assumption was the bug.
Also corrected the function's own doc comment and the std::hash API
docs, which described the old behavior as intended.

Fixes #5400

Signed-off-by: Afonso Januário <afonso-januario@hotmail.com>
Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Remove now-unused number_integer_t/number_unsigned_t typedefs in hash()

Merging the three numeric branches into one that only reads
number_float_t left these two aliases unused, which several CI
configurations treat as a build error under -Wunused-local-typedefs.

Signed-off-by: Afonso Januário <afonso-januario@hotmail.com>
Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Mark the unordered_set in the hash regression test const

clang-tidy's misc-const-correctness check flagged it: the set is
never mutated after construction, only read via size().

Signed-off-by: Afonso Januário <afonso-januario@hotmail.com>
Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Normalize -0.0 in number hashes and test range ends

operator== compares numbers exactly since #5459, so equal numbers
share one value and convert to the same number_float_t. Update the
comment accordingly, map -0.0 to 0.0 before hashing (std::hash need
not do that), and test -0.0 and the ends of the integer ranges. Show
hash(0.0) in the docs example and note the change in the version
history.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Clarify hash documentation after review

- Say "may hash differently" for null, false, and numbers, since a
  collision across types is possible.
- Name the storage types (signed integer, unsigned integer,
  floating-point number) instead of example literals.
- Explain that the hash survives converting an integer to
  number_float_t but not the lossy conversion back, and that unequal
  numbers may share a hash.
- State that the example hash values are illustrative only and vary by
  platform, compiler, compiler version, and library version.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

---------

Signed-off-by: Afonso Januário <afonso-januario@hotmail.com>
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
Co-authored-by: Afonso Januário <afonso-januario@hotmail.com>
2026-10-07 19:18:51 +02:00

1.8 KiB

std::hashnlohmann::basic_json\

namespace std {
    struct hash<nlohmann::basic_json>;
}

Return a hash value for a JSON object. The hash function tries to rely on std::hash where possible. Furthermore, the type of the JSON value is taken into account, so #!json null, #!cpp false, and numbers may hash differently from each other. Numbers that compare equal under operator== always hash equally, regardless of whether they are stored as signed integer, unsigned integer, or floating-point number.

Numbers are hashed by their value converted to number_float_t. Converting an integer to number_float_t therefore keeps its hash, but converting a floating-point number to an integer type is lossy and can change it: #!cpp 0.5 converts to #!cpp 0, which need not have the same hash. Unequal numbers can also share a hash value, for example two large integers that convert to the same number_float_t.

Examples

??? example

The example shows how to calculate hash values for different JSON values.
 
```cpp
--8<-- "examples/std_hash.cpp"
```

Output:

```json
--8<-- "examples/std_hash.output"
```

The hash values shown are examples only. They depend on the platform, the compiler, and the compiler version, and
they can change between versions of this library. Do not persist them or rely on specific values.

See also

  • operator== compares two JSON values for equality, consistent with equal hash values

Version history

  • Added in version 1.0.0.
  • Extended for arbitrary basic_json types in version 3.10.5.
  • Numbers that compare equal hash equally since version 3.13.0; before, #!cpp 0, #!cpp 0U, and #!cpp 0.0 had different hash values.