-
Notifications
You must be signed in to change notification settings - Fork 12
Feature: util hash #92
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,239 @@ | ||
| --- | ||
| title: std::hash | ||
| cppdoc: | ||
| revision: | ||
| since: C++11 | ||
| --- | ||
|
|
||
| import { CppHeader } from "@components/header"; | ||
| import { Decl, DeclDoc } from "@components/decl-doc"; | ||
| import { Desc, DescList } from "@components/desc-list"; | ||
| import Missing from "@components/Missing.astro"; | ||
| import { ParamDoc, ParamDocList } from "@components/param-doc"; | ||
| import { Revision, RevisionBlock } from "@components/revision"; | ||
| import DocLink from "@components/DocLink.astro" | ||
|
|
||
| Defined in header <CppHeader name="bitset" />. | ||
|
|
||
| Defined in header <CppHeader name="coroutine" />. | ||
|
|
||
| Defined in header <CppHeader name="chrono" />. | ||
|
|
||
| Defined in header <CppHeader name="filesystem" />. | ||
|
|
||
| Defined in header <CppHeader name="functional" />. | ||
|
|
||
| Defined in header <CppHeader name="memory" />. | ||
|
|
||
| Defined in header <CppHeader name="optional" />. | ||
|
|
||
| Defined in header <CppHeader name="stacktrace" />. | ||
|
|
||
| Defined in header <CppHeader name="string" />. | ||
|
|
||
| Defined in header <CppHeader name="string_view" />. | ||
|
|
||
| Defined in header <CppHeader name="system_error" />. | ||
|
|
||
| Defined in header <CppHeader name="text_encoding" />. | ||
|
|
||
| Defined in header <CppHeader name="thread" />. | ||
|
|
||
| Defined in header <CppHeader name="typeindex" />. | ||
|
|
||
| Defined in header <CppHeader name="variant" />. | ||
|
|
||
| Defined in header <CppHeader name="vector" />. | ||
|
|
||
| <DeclDoc> | ||
| <Decl slot="decl"> | ||
| <RevisionBlock since="C++11" noborder> | ||
| ```cpp | ||
| template<class Key> | ||
| struct hash; | ||
| ``` | ||
| </RevisionBlock> | ||
| </Decl> | ||
|
|
||
| The unordered associative containers `std::unordered_set`, `std::unordered_multiset`, `std::unordered_map`, `std::unordered_multimap` use specializations of the template std::hash as the default hash function. | ||
| </DeclDoc> | ||
|
|
||
| Given a type `Key`, each specialization `std::hash<Key>` is either _enabled_ or _disabled_ : | ||
|
|
||
| - If `std::hash<Key>` is not provided by the program or the user, it is disabled. | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please leave only one space between |
||
| - Otherwise, `std::hash<Key>` is enabled if all following conditions are satisfied: | ||
| - All following requirements are satisfied: | ||
| - [Hash](../named_req/Hash.html "cpp/named req/Hash") (with `Key` as the function call argument type) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please use |
||
| - [DefaultConstructible](../named_req/DefaultConstructible.html "cpp/named req/DefaultConstructible") | ||
| - [CopyAssignable](../named_req/CopyAssignable.html "cpp/named req/CopyAssignable") | ||
| - [Swappable](../named_req/Swappable.html "cpp/named req/Swappable") | ||
|
|
||
| - Given the following values: | ||
| - `h`, an object of type `std::hash<Key>`. | ||
| - `k1` and `k2`, objects of type `Key`. | ||
| All following requirements are satisfied: | ||
| - If `k1 == k2` is true, `h(k1) == h(k2)` is also true. | ||
| - Unless `std::hash<Key>` is a [program-defined specialization](../language/type-id.html#Program-defined_type "cpp/language/type"), h(k1) will never throw an exception. | ||
|
|
||
| - Otherwise, `std::hash<Key>` is disabled. | ||
|
|
||
| Disabled specializations do not satisfy [Hash](../named_req/Hash.html "cpp/named req/Hash"), do not satisfy [FunctionObject](../named_req/FunctionObject.html "cpp/named req/FunctionObject"), and following values are all false: | ||
|
|
||
| - `std::is_default_constructible<std::hash<Key>>::value` | ||
| - `std::is_copy_constructible<std::hash<Key>>::value` | ||
| - `std::is_move_constructible<std::hash<Key>>::value` | ||
| - `std::is_copy_assignable<std::hash<Key>>::value` | ||
| - `std::is_move_assignable<std::hash<Key>>::value` | ||
|
|
||
| In other words, they exist, but cannot be used. | ||
| <DeclDoc> | ||
| <Decl slot="decl"> | ||
| <RevisionBlock until="C++20" noborder> | ||
| Nested types: | ||
| |Name|Definition| | ||
| |---|---| | ||
| |`argument_type` (deprecared in C++ 17)| Key| | ||
| |`result_type` (deprecared in C++ 17)| size_t| | ||
| </RevisionBlock> | ||
| </Decl> | ||
| </DeclDoc> | ||
|
|
||
| ### Member functions | ||
| | | | | ||
| |---|---| | ||
| |(constuctor) | <Desc kind="public member function">constructs a hash function object </Desc>| | ||
| |<DocLink dest="/cpp/library/utility/hash/operator-call"> `operator()` </DocLink> | <Desc kind="public member function">calculates the hash of the argument </Desc>| | ||
|
Comment on lines
+102
to
+105
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here. This is not right. We need to implement a component for such content. |
||
|
|
||
|
|
||
| ### Standard library specializations | ||
| Each header that declares the template std::hash also provides enabled specializations of std::hash for the following types: | ||
|
|
||
| - all cv-unqualified arithmetic types | ||
| - all cv-unqualified enumeration types | ||
| - all cv-unqualified pointer types | ||
| - std::nullptr_t | ||
|
|
||
| On top of that, some headers also provide other enabled std::hash specializations for library types (see below). | ||
|
|
||
|
|
||
|
|
||
| <DeclDoc> | ||
| <Decl slot="decl"> | ||
| <RevisionBlock since="C++17" noborder> | ||
| For all std::hash specializations provided by the standard library except the following, all their member functions are noexcept: | ||
| - `std::hash<std::optional>` | ||
| - `std::hash<std::variant>` | ||
| - `std::hash<std::unique_ptr>` | ||
|
|
||
| <RevisionBlock since="c++26"> | ||
| - `std::hash<std::chrono::duration>` | ||
| - `std::hash<std::chrono::time_point>` | ||
| - `std::hash<std::chrono::zoned_time>` | ||
| </RevisionBlock> | ||
| </RevisionBlock> | ||
| </Decl> | ||
| </DeclDoc> | ||
|
|
||
|
|
||
| ## Notes | ||
|
|
||
| The actual hash functions are implementation-dependent and are not required to fulfill any other quality criteria except those specified above. Notably, some implementations use trivial (identity) hash functions which map an integer to itself. In other words, these hash functions are designed to work with unordered associative containers, but not as cryptographic hashes, for example. | ||
|
|
||
| Hash functions are only required to produce the same result for the same input within a single execution of a program; this allows salted hashes that prevent collision denial-of-service attacks. | ||
|
|
||
| There is no specialization for C strings. `std::hash<const char*>` produces a hash of the value of the pointer (the memory address), it does not examine the contents of any character array. | ||
|
|
||
| Additional specializations for `std::pair` and the standard container types, as well as utility functions to compose hashes are available in `boost::hash`. | ||
|
|
||
|
|
||
| ## Example | ||
|
|
||
| ```cpp | ||
| #include <cstddef> | ||
| #include <functional> | ||
| #include <iomanip> | ||
| #include <iostream> | ||
| #include <string> | ||
| #include <unordered_set> | ||
|
|
||
| struct S | ||
| { | ||
| std::string first_name; | ||
| std::string last_name; | ||
| bool operator==(const S&) const = default; // since C++20 | ||
| }; | ||
|
|
||
| // Before C++20. | ||
| // bool operator==(const S& lhs, const S& rhs) | ||
| // { | ||
| // return lhs.first_name == rhs.first_name && lhs.last_name == rhs.last_name; | ||
| // } | ||
|
|
||
| // Custom hash can be a standalone function object. | ||
| struct MyHash | ||
| { | ||
| std::size_t operator()(const S& s) const noexcept | ||
| { | ||
| std::size_t h1 = std::hash<std::string>{}(s.first_name); | ||
| std::size_t h2 = std::hash<std::string>{}(s.last_name); | ||
| return h1 ^ (h2 << 1); // or use boost::hash_combine | ||
| } | ||
| }; | ||
|
|
||
| // Custom specialization of std::hash can be injected in namespace std. | ||
| template<> | ||
| struct std::hash<S> | ||
| { | ||
| std::size_t operator()(const S& s) const noexcept | ||
| { | ||
| std::size_t h1 = std::hash<std::string>{}(s.first_name); | ||
| std::size_t h2 = std::hash<std::string>{}(s.last_name); | ||
| return h1 ^ (h2 << 1); // or use boost::hash_combine | ||
| } | ||
| }; | ||
|
|
||
| int main() | ||
| { | ||
| std::string str = "Meet the new boss..."; | ||
| std::size_t str_hash = std::hash<std::string>{}(str); | ||
| std::cout << "hash(" << std::quoted(str) << ") =\t" << str_hash << '\n'; | ||
|
|
||
| S obj = {"Hubert", "Farnsworth"}; | ||
| // Using the standalone function object. | ||
| std::cout << "hash(" << std::quoted(obj.first_name) << ", " | ||
| << std::quoted(obj.last_name) << ") =\t" | ||
| << MyHash{}(obj) << " (using MyHash) or\n\t\t\t\t" | ||
| << std::hash<S>{}(obj) << " (using injected specialization)\n"; | ||
|
|
||
| // Custom hash makes it possible to use custom types in unordered containers. | ||
| // The example will use the injected std::hash<S> specialization above, | ||
| // to use MyHash instead, pass it as a second template argument. | ||
| std::unordered_set<S> names = {obj, {"Bender", "Rodriguez"}, {"Turanga", "Leela"}}; | ||
| for (auto const& s: names) | ||
| std::cout << std::quoted(s.first_name) << ' ' | ||
| << std::quoted(s.last_name) << '\n'; | ||
| } | ||
| ``` | ||
|
|
||
| Possible outputs: | ||
|
|
||
| ```cpp | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This code block does not contain C++ source code. |
||
| hash("Meet the new boss...") = 10656026664466977650 | ||
| hash("Hubert", "Farnsworth") = 12922914235676820612 (using MyHash) or | ||
| 12922914235676820612 (using injected specialization) | ||
| "Bender" "Rodriguez" | ||
| "Turanga" "Leela" | ||
| "Hubert" "Farnsworth" | ||
| ``` | ||
|
|
||
| ## Defect reports | ||
|
|
||
| The following behavior-changing defect reports were applied retroactively to previously published C++ standards. | ||
|
|
||
| | DR | Applied to | Behavior as published | Correct behavior | | ||
| | --- | --- | --- | --- | | ||
| | [LWG 2119](https://cplusplus.github.io/LWG/issue2119) | C++11 | specializations for extended integer types were missing | provided | | ||
| | [LWG 2148](https://cplusplus.github.io/LWG/issue2148) | C++11 | specializations for enumerations were missing | provided | | ||
| | [LWG 2543](https://cplusplus.github.io/LWG/issue2543) | C++11 | `std::hash` might not be SFINAE-friendly | made SFINAE-friendly | | ||
| | [LWG 2817](https://cplusplus.github.io/LWG/issue2817) | C++11 | specialization for [std::nullptr\_t](../types/nullptr_t.html "cpp/types/nullptr_t") was missing | provided | | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please use |
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,83 @@ | ||
| --- | ||
| title: std::hash::operator() | ||
| cppdoc: | ||
| revision: | ||
| since: C++11 | ||
| --- | ||
|
|
||
| import { ParamDoc, ParamDocList } from "@components/param-doc"; | ||
|
|
||
| Specializations of `std::hash` should define an `operator()` that: | ||
|
|
||
| - Takes a single argument key of type `Key`. | ||
| - Returns a value of type `std::size_t` that represents the hash value of key. | ||
| - For two parameters `k1` and `k2` that are equal, `std::hash<Key>()(k1) == std::hash<Key>()(k2)`. | ||
| - For two different parameters `k1` and `k2` that are not equal, the probability that `std::hash<Key>()(k1) == std::hash<Key>()(k2)` should be very small, approaching `1.0 / std::numeric_limits<size_t>::max()`. | ||
|
|
||
|
|
||
| ## Parameters | ||
| <ParamDocList> | ||
| <ParamDoc name="key"> | ||
| the object to be hashed | ||
| </ParamDoc> | ||
| </ParamDocList> | ||
|
|
||
| ## Return value | ||
| A `std::size_t` representing the hash value. | ||
|
|
||
| ## Exceptions | ||
| Hash functions should not throw exceptions. | ||
|
|
||
| ## Example | ||
| The following code shows how to specialize the std::hash template for a custom class. The hash function uses Fowler–Noll–Vo hash algorithm. | ||
|
|
||
| ```cpp | ||
| #include <cstdint> | ||
| #include <functional> | ||
| #include <iostream> | ||
| #include <string> | ||
|
|
||
| struct Employee | ||
| { | ||
| std::string name; | ||
| std::uint64_t ID; | ||
| }; | ||
|
|
||
| namespace std | ||
| { | ||
| template <> | ||
| class hash<Employee> | ||
| { | ||
| public: | ||
| std::uint64_t operator()(const Employee& employee) const | ||
| { | ||
| // computes the hash of an employee using a variant | ||
| // of the Fowler-Noll-Vo hash function | ||
| constexpr std::uint64_t prime{0x100000001B3}; | ||
| std::uint64_t result{0xcbf29ce484222325}; | ||
|
|
||
| for (std::uint64_t i{}, ie = employee.name.size(); i != ie; ++i) | ||
| result = (result * prime) ^ employee.name[i]; | ||
|
|
||
| return result ^ (employee.ID << 1); | ||
| } | ||
| }; | ||
| } | ||
|
|
||
| int main() | ||
| { | ||
| Employee employee; | ||
| employee.name = "Zaphod Beeblebrox"; | ||
| employee.ID = 42; | ||
|
|
||
| std::hash<Employee> hash_fn; | ||
| std::cout << hash_fn(employee) << '\n'; | ||
| } | ||
| ``` | ||
|
|
||
| Output: | ||
|
|
||
| ```cpp | ||
| 12615575401975788567 | ||
| ``` | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This series of header files take too much screen space on the page. I think we have to come up with a way to get around this.