Difference between revisions of "cpp/utility/hash"
m (tt) |
(Applied P2592R3 (Hashing support for std::chrono value classes) part 3.) |
||
Line 11: | Line 11: | ||
# Accepts a single parameter of type {{tt|Key}}. | # Accepts a single parameter of type {{tt|Key}}. | ||
− | # Returns a value of type {{ | + | # Returns a value of type {{lc|std::size_t}} that represents the hash value of the parameter. |
# Does not throw exceptions when called. | # Does not throw exceptions when called. | ||
− | # For two parameters {{ | + | # For two parameters {{c|k1}} and {{c|k2}} that are equal, {{c|1=std::hash<Key>()(k1) == std::hash<Key>()(k2)}}. |
− | # For two different parameters {{ | + | # For two different parameters {{c|k1}} and {{c|k2}} that are not equal, the probability that {{c|1=std::hash<Key>()(k1) == std::hash<Key>()(k2)}} should be very small, approaching {{c|1.0 / std::numeric_limits<std::size_t>::max()}}. |
− | All explicit and partial specializations of {{tt|hash}} provided by the standard library are {{named req|DefaultConstructible}}, {{named req|CopyAssignable}}, {{named req|Swappable}} and {{named req|Destructible}}. User-provided specializations of {{tt|hash}} also must meet those requirements. | + | All explicit and partial specializations of {{tt|std::hash}} provided by the standard library are {{named req|DefaultConstructible}}, {{named req|CopyAssignable}}, {{named req|Swappable}} and {{named req|Destructible}}. User-provided specializations of {{tt|hash}} also must meet those requirements. |
The unordered associative containers {{lc|std::unordered_set}}, {{lc|std::unordered_multiset}}, {{lc|std::unordered_map}}, {{lc|std::unordered_multimap}} use specializations of the template {{tt|std::hash}} as the default hash function. | The unordered associative containers {{lc|std::unordered_set}}, {{lc|std::unordered_multiset}}, {{lc|std::unordered_map}}, {{lc|std::unordered_multimap}} use specializations of the template {{tt|std::hash}} as the default hash function. | ||
Line 34: | Line 34: | ||
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. | 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. {{c|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. | + | There is no specialization for C strings. {{c/core|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. |
{{rrev|until=c++20|1= | {{rrev|until=c++20|1= | ||
Line 79: | Line 79: | ||
{{dcl end}} | {{dcl end}} | ||
− | In addition to the above, the standard library provides specializations for all (scoped and unscoped) enumeration types. These may be (but are not required to be) implemented as {{c|std::hash<std::underlying_type<Enum>::type>}}. | + | In addition to the above, the standard library provides specializations for all (scoped and unscoped) enumeration types. These may be (but are not required to be) implemented as {{c/core|std::hash<std::underlying_type<Enum>::type>}}. |
The standard library provides enabled specializations of {{tt|std::hash}} for {{lc|std::nullptr_t}} and all cv-unqualified arithmetic types (including any extended integer types), all enumeration types, and all pointer types. | The standard library provides enabled specializations of {{tt|std::hash}} for {{lc|std::nullptr_t}} and all cv-unqualified arithmetic types (including any extended integer types), all enumeration types, and all pointer types. | ||
− | Each standard library header that declares the template {{tt|std::hash}} provides all enabled specializations described above | + | Each standard library header that declares the template {{tt|std::hash}} provides all enabled specializations described above: |
+ | * {{header|bitset}} | ||
+ | * {{header|memory}} | ||
+ | * {{header|string}} | ||
+ | * {{header|system_error}} | ||
+ | * {{header|thread}} | ||
+ | * {{header|typeindex}} | ||
+ | * {{header|vector}} | ||
+ | {{rev begin}} | ||
+ | {{rev|since=c++17| | ||
+ | * {{header|optional}} | ||
+ | * {{header|string_view}} | ||
+ | * {{header|variant}} | ||
+ | }} | ||
+ | {{rev|since=c++20| | ||
+ | * {{header|coroutine}} | ||
+ | }} | ||
+ | {{rev|since=c++23| | ||
+ | * {{header|stacktrace}} | ||
+ | }} | ||
+ | {{rev|since=c++26| | ||
+ | * {{header|chrono}} | ||
+ | }} | ||
+ | {{rev end}} | ||
+ | |||
{{rrev|since=c++17|<!-- P0599R1, not a DR in P0636R2 --> | {{rrev|since=c++17|<!-- P0599R1, not a DR in P0636R2 --> | ||
− | All member functions of all standard library specializations of this template are {{c|noexcept}} except for the member functions | + | All member functions of all standard library specializations of this template are {{c/core|noexcept}} except for the member functions of: |
+ | * {{ltt|cpp/utility/optional/hash|std::hash<std::optional>}} | ||
+ | * {{ltt|cpp/utility/variant/hash|std::hash<std::variant>}} | ||
+ | * {{ltt|cpp/memory/unique_ptr/hash|std::hash<std::unique_ptr>}} | ||
+ | {{rrev|since=c++26| | ||
+ | * {{ltt|cpp/chrono/duration/hash|std::hash<std::chrono::duration>}} | ||
+ | * {{ltt|cpp/chrono/time_point/hash|std::hash<std::chrono::time_point>}} | ||
+ | * {{ltt|cpp/chrono/zoned_time/hash|std::hash<std::chrono::zoned_time>}} | ||
+ | }} | ||
}} | }} | ||
Line 105: | Line 137: | ||
{{dsc inc|cpp/string/basic_string/dsc hash}} | {{dsc inc|cpp/string/basic_string/dsc hash}} | ||
{{dsc inc|cpp/string/basic_string_view/dsc hash}} | {{dsc inc|cpp/string/basic_string_view/dsc hash}} | ||
− | {{dsc ptclass|cpp/container/vector bool/hash|title=std::hash{{dsc small|<std::vector<bool>>}}|hash support for {{ | + | {{dsc ptclass|cpp/container/vector bool/hash|title=std::hash{{dsc small|<std::vector<bool>>}}|hash support for {{ltt|cpp/container/vector bool|std::vector<bool>}}|notes={{mark c++11}}}} |
{{dsc inc|cpp/filesystem/path/dsc hash}} | {{dsc inc|cpp/filesystem/path/dsc hash}} | ||
{{dsc inc|cpp/thread/thread/id/dsc hash}} | {{dsc inc|cpp/thread/thread/id/dsc hash}} | ||
+ | {{dsc hash|cpp/chrono/duration|nested=true|notes={{mark c++26}}}} | ||
+ | {{dsc hash|cpp/chrono/time_point|nested=true|notes={{mark c++26}}}} | ||
+ | {{dsc hash|cpp/chrono/day|nested=true|notes={{mark c++26}}}} | ||
+ | {{dsc hash|cpp/chrono/month|nested=true|notes={{mark c++26}}}} | ||
+ | {{dsc hash|cpp/chrono/year|nested=true|notes={{mark c++26}}}} | ||
+ | {{dsc hash|cpp/chrono/weekday|nested=true|notes={{mark c++26}}}} | ||
+ | {{dsc hash|cpp/chrono/weekday_indexed|nested=true|notes={{mark c++26}}}} | ||
+ | {{dsc hash|cpp/chrono/weekday_last|nested=true|notes={{mark c++26}}}} | ||
+ | {{dsc hash|cpp/chrono/month_day|nested=true|notes={{mark c++26}}}} | ||
+ | {{dsc hash|cpp/chrono/month_day_last|nested=true|notes={{mark c++26}}}} | ||
+ | {{dsc hash|cpp/chrono/month_weekday|nested=true|notes={{mark c++26}}}} | ||
+ | {{dsc hash|cpp/chrono/month_weekday_last|nested=true|notes={{mark c++26}}}} | ||
+ | {{dsc hash|cpp/chrono/year_month|nested=true|notes={{mark c++26}}}} | ||
+ | {{dsc hash|cpp/chrono/year_month_day|nested=true|notes={{mark c++26}}}} | ||
+ | {{dsc hash|cpp/chrono/year_month_day_last|nested=true|notes={{mark c++26}}}} | ||
+ | {{dsc hash|cpp/chrono/year_month_weekday|nested=true|notes={{mark c++26}}}} | ||
+ | {{dsc hash|cpp/chrono/year_month_weekday_last|nested=true|notes={{mark c++26}}}} | ||
+ | {{dsc hash|cpp/chrono/zoned_time|nested=true|notes={{mark c++26}}}} | ||
+ | {{dsc hash|cpp/chrono/leap_second|nested=true|notes={{mark c++26}}}} | ||
{{dsc end}} | {{dsc end}} | ||
Line 138: | Line 189: | ||
struct MyHash | struct MyHash | ||
{ | { | ||
− | std::size_t operator()( | + | std::size_t operator()(const S& s) const noexcept |
{ | { | ||
std::size_t h1 = std::hash<std::string>{}(s.first_name); | std::size_t h1 = std::hash<std::string>{}(s.first_name); | ||
Line 150: | Line 201: | ||
struct std::hash<S> | struct std::hash<S> | ||
{ | { | ||
− | std::size_t operator()( | + | std::size_t operator()(const S& s) const noexcept |
{ | { | ||
std::size_t h1 = std::hash<std::string>{}(s.first_name); | std::size_t h1 = std::hash<std::string>{}(s.first_name); | ||
Line 164: | Line 215: | ||
std::cout << "hash(" << std::quoted(str) << ") =\n\t" << str_hash << '\n'; | std::cout << "hash(" << std::quoted(str) << ") =\n\t" << str_hash << '\n'; | ||
− | S obj = { "Hubert", "Farnsworth" }; | + | S obj = {"Hubert", "Farnsworth"}; |
// using the standalone function object | // using the standalone function object | ||
std::cout << "hash(" << std::quoted(obj.first_name) << ", " | std::cout << "hash(" << std::quoted(obj.first_name) << ", " | ||
<< std::quoted(obj.last_name) << ") =\n\t" | << std::quoted(obj.last_name) << ") =\n\t" | ||
<< MyHash{}(obj) << " (using MyHash) or\n\t" | << MyHash{}(obj) << " (using MyHash) or\n\t" | ||
− | << std::hash<S>{}(obj) << " (using injected | + | << std::hash<S>{}(obj) << " (using injected specialization)\n"; |
// Custom hash makes it possible to use custom types in unordered containers. | // Custom hash makes it possible to use custom types in unordered containers. | ||
// The example will use the injected std::hash<S> specialization above, | // The example will use the injected std::hash<S> specialization above, | ||
// to use MyHash instead, pass it as a second template argument. | // to use MyHash instead, pass it as a second template argument. | ||
− | std::unordered_set<S> names = { obj, {"Bender", "Rodriguez"}, {"Turanga", "Leela"} }; | + | std::unordered_set<S> names = {obj, {"Bender", "Rodriguez"}, {"Turanga", "Leela"}<!---->}; |
for (auto const& s: names) | for (auto const& s: names) | ||
− | std::cout << std::quoted(s.first_name) << ' ' << std::quoted(s.last_name) << '\n'; | + | std::cout << std::quoted(s.first_name) << ' ' |
+ | << std::quoted(s.last_name) << '\n'; | ||
} | } | ||
|p=true | |p=true | ||
|output= | |output= | ||
hash("Meet the new boss...") = | hash("Meet the new boss...") = | ||
− | + | 1861821886482076440 | |
hash("Hubert", "Farnsworth") = | hash("Hubert", "Farnsworth") = | ||
− | + | 17622465712001802105 (using MyHash) or | |
− | + | 17622465712001802105 (using injected specialization) | |
"Turanga" "Leela" | "Turanga" "Leela" | ||
"Bender" "Rodriguez" | "Bender" "Rodriguez" | ||
Line 193: | Line 245: | ||
{{dr list begin}} | {{dr list begin}} | ||
{{dr list item|wg=lwg|dr=2148|std=C++11|before=specializations for enumerations were missing|after=provided}} | {{dr list item|wg=lwg|dr=2148|std=C++11|before=specializations for enumerations were missing|after=provided}} | ||
− | {{dr list item|wg=lwg|dr=2543|std=C++11|before={{tt|hash}} might not be SFINAE-friendly|after=made SFINAE-friendly via disabled specializations}} | + | {{dr list item|wg=lwg|dr=2543|std=C++11|before={{tt|std::hash}} might not be SFINAE-friendly|after=made SFINAE-friendly via disabled specializations}} |
− | {{dr list item|wg=lwg|dr=2817|std=C++11|before=specialization for {{ | + | {{dr list item|wg=lwg|dr=2817|std=C++11|before=specialization for {{lc|std::nullptr_t}} was missing|after=provided}} |
{{dr list end}} | {{dr list end}} | ||
{{langlinks|de|es|fr|it|ja|pt|ru|zh}} | {{langlinks|de|es|fr|it|ja|pt|ru|zh}} |
Revision as of 01:28, 29 August 2023
Defined in header <functional>
|
||
template< class Key > struct hash; |
(since C++11) | |
Each specialization of this template is either enabled ("untainted") or disabled ("poisoned").
The enabled specializations of the hash
template defines a function object that implements a Hash function. Instances of this function object satisfy Hash. In particular, they define an operator() const that:
- Accepts a single parameter of type
Key
. - Returns a value of type std::size_t that represents the hash value of the parameter.
- Does not throw exceptions when called.
- 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<std::size_t>::max().
All explicit and partial specializations of std::hash
provided by the standard library are DefaultConstructible, CopyAssignable, Swappable and Destructible. User-provided specializations of hash
also must meet those requirements.
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.
For every type Key
for which neither the library nor the user provides an enabled specialization std::hash<Key>
, that specialization exists and is disabled. Disabled specializations do not satisfy Hash, do not satisfy 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.
Contents |
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.
Member types
|
(until C++20) |
Member functions
constructs a hash function object (public member function) | |
calculates the hash of the argument (public member function) |
Standard specializations for basic types
Defined in header <functional>
|
||
template<> struct hash<bool>; template<> struct hash<char>; |
||
In addition to the above, the standard library provides specializations for all (scoped and unscoped) enumeration types. These may be (but are not required to be) implemented as std::hash<std::underlying_type<Enum>::type>.
The standard library provides enabled specializations of std::hash
for std::nullptr_t and all cv-unqualified arithmetic types (including any extended integer types), all enumeration types, and all pointer types.
Each standard library header that declares the template std::hash
provides all enabled specializations described above:
(since C++17) | |
(since C++20) | |
(since C++23) | |
(since C++26) |
All member functions of all standard library specializations of this template are noexcept except for the member functions of:
|
(since C++17) |
Standard specializations for library types
hash support for std::coroutine_handle (class template specialization) | |
(C++11) |
hash support for std::error_code (class template specialization) |
hash support for std::error_condition (class template specialization) | |
hash support for std::stacktrace_entry (class template specialization) | |
hash support for std::basic_stacktrace (class template specialization) | |
(C++17) |
hash support for std::optional (class template specialization) |
(C++17) |
hash support for std::variant (class template specialization) |
(C++17) |
hash support for std::monostate (class template specialization) |
(C++11) |
hash support for std::bitset (class template specialization) |
(C++11) |
hash support for std::unique_ptr (class template specialization) |
(C++11) |
hash support for std::shared_ptr (class template specialization) |
(C++11) |
hash support for std::type_index (class template specialization) |
(C++11) |
hash support for strings (class template specialization) |
hash support for string views (class template specialization) | |
(C++11) |
hash support for std::vector<bool> (class template specialization) |
hash support for std::filesystem::path (class template specialization) | |
(C++11) |
hash support for std::thread::id (class template specialization) |
hash support for std::chrono::duration (class template specialization) | |
hash support for std::chrono::time_point (class template specialization) | |
(C++26) |
hash support for std::chrono::day (class template specialization) |
hash support for std::chrono::month (class template specialization) | |
(C++26) |
hash support for std::chrono::year (class template specialization) |
hash support for std::chrono::weekday (class template specialization) | |
hash support for std::chrono::weekday_indexed (class template specialization) | |
hash support for std::chrono::weekday_last (class template specialization) | |
hash support for std::chrono::month_day (class template specialization) | |
hash support for std::chrono::month_day_last (class template specialization) | |
hash support for std::chrono::month_weekday (class template specialization) | |
hash support for std::chrono::month_weekday_last (class template specialization) | |
hash support for std::chrono::year_month (class template specialization) | |
hash support for std::chrono::year_month_day (class template specialization) | |
hash support for std::chrono::year_month_day_last (class template specialization) | |
hash support for std::chrono::year_month_weekday (class template specialization) | |
hash support for std::chrono::year_month_weekday_last (class template specialization) | |
hash support for std::chrono::zoned_time (class template specialization) | |
hash support for std::chrono::leap_second (class template specialization) |
Note: additional specializations for std::pair and the standard container types, as well as utility functions to compose hashes are available in boost::hash
.
Example
#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) << ") =\n\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) << ") =\n\t" << MyHash{}(obj) << " (using MyHash) or\n\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 output:
hash("Meet the new boss...") = 1861821886482076440 hash("Hubert", "Farnsworth") = 17622465712001802105 (using MyHash) or 17622465712001802105 (using injected specialization) "Turanga" "Leela" "Bender" "Rodriguez" "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 2148 | C++11 | specializations for enumerations were missing | provided |
LWG 2543 | C++11 | std::hash might not be SFINAE-friendly
|
made SFINAE-friendly via disabled specializations |
LWG 2817 | C++11 | specialization for std::nullptr_t was missing | provided |