Modern C++ brought us std::hash template (read more about it here). In short: it’s a stateless function object that implements operator() which takes an instance of a type as parameter and returns its hash as size_t. It has specializations for all primitive types as well as some library types. You can also specialize it yourself for your own data types (don’t forget to put your specialization in namespace std). Let’s see how it works by hashing some ints, chars, floats, pointers, strings, and our own custom data type. Pay close attention to the hash values of ints and chars…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
#include <iostream> #include <string> #include <functional> using namespace std; struct H { string s1, s2; }; ostream& operator << (ostream& os, const H& h) { os << h.s1 << "," << h.s2; return os; } namespace std { template <> struct hash<H> { size_t operator()(const H& h) const { return hash<string>{}(h.s1) ^ (hash<string>{}(h.s2) << 1); } }; } int main() { for(int i = 1; i <= 3; ++i) cout << "Hash of '" << i << "': " << hash<int>{}(i) << endl; for(char c = 'A'; c <= 'C'; ++c) cout << "Hash of '" << c << "': " << hash<char>{}(c) << endl; for(float f = 1.1; f < 1.4; f += 0.1) cout << "Hash of '" << f << "': " << hash<float>{}(f) << endl; char* p = new char[3]; char* q = p; for(; p < q + 3; ++p) cout << "Hash of '" << (int*)p << "': " << hash<char*>{}(p) << endl; string s1 = "Vorbrodt's C++ Blog"; string s2 = "Vorbrodt's C++ Blog"; string s3 = "https://vorbrodt.blog"; cout << "Hash of '" << s1 << "': " << hash<string>{}(s1) << endl; cout << "Hash of '" << s2 << "': " << hash<string>{}(s2) << endl; cout << "Hash of '" << s3 << "': " << hash<string>{}(s3) << endl; H h1{"Vorbrodt's C++ Blog", "https://vorbrodt.blog"}; H h2{"Vorbrodt's C++ Blog", "https://vorbrodt.blog"}; H h3{"https://vorbrodt.blog", "Vorbrodt's C++ Blog"}; cout << "Hash of '" << h1 << "': " << hash<H>{}(h1) << endl; cout << "Hash of '" << h2 << "': " << hash<H>{}(h2) << endl; cout << "Hash of '" << h3 << "': " << hash<H>{}(h3) << endl; } |
Hash of ‘1’: 1
Hash of ‘2’: 2
Hash of ‘3’: 3
Hash of ‘A’: 65
Hash of ‘B’: 66
Hash of ‘C’: 67
Hash of ‘1.1’: 1066192077
Hash of ‘1.2’: 1067030938
Hash of ‘1.3’: 1067869799
Hash of ‘0x7f95fdd000a0’: 6424303057458324486
Hash of ‘0x7f95fdd000a1’: 6736290418105006831
Hash of ‘0x7f95fdd000a2’: 13890240933949840298
Hash of ‘Vorbrodt’s C++ Blog’: 435643587581864924
Hash of ‘Vorbrodt’s C++ Blog’: 435643587581864924
Hash of ‘https://vorbrodt.blog’: 13293888041758778516
Hash of ‘Vorbrodt’s C++ Blog,https://vorbrodt.blog’: 8570762348687434484
Hash of ‘Vorbrodt’s C++ Blog,https://vorbrodt.blog’: 8570762348687434484
Hash of ‘https://vorbrodt.blog,Vorbrodt’s C++ Blog’: 13000220508453909292
Interesting to note: Compilers, such as GCC or clang are using the identity as hash (https://en.cppreference.com/w/cpp/utility/hash), they map integers to themselve. MSVC on the other hand is performing real hashing