Rule A18-1-6 (required, implementation, automated)

All std::hash specializations for user-defined types shall have a noexcept function call operator.

Rationale

Some of standard library containers use std::hash indirectly. Function call operator should be defined as noexcept to prevent container simple access from throwing an exception. Note: Consider own hash specializations to use standard library specializations combined with XOR (ˆ) operation as implemented by boost::hash_combine: seed ^= std::hash< decltype (v)>{}(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);

Example

// $Id: A18-1-6.cpp 311792 2018-03-15 04:15:08Z christof.meerwald $ #include <cstdint> #include <functional> #include <string> #include <unordered_map> class A { public: A(uint32_t x, uint32_t y) noexcept : x(x), y(y) {} uint32_t GetX() const noexcept { return x; } uint32_t GetY() const noexcept { return y; } friend bool operator == (const A &lhs, const A &rhs) noexcept { return lhs.x == rhs.x && lhs.y == rhs.y; } private: uint32_t x; uint32_t y; }; class B { public: B(uint32_t x, uint32_t y) noexcept : x(x), y(y) {} uint32_t GetX() const noexcept { return x; } uint32_t GetY() const noexcept { return y; } friend bool operator == (const B &lhs, const B &rhs) noexcept { return lhs.x == rhs.x && lhs.y == rhs.y; } private: uint32_t x; uint32_t y; }; namespace std { // Compliant template <> struct hash<A> { std::size_t operator()(const A& a) const noexcept { auto h1 = std::hash<decltype(a.GetX())>{}(a.GetX()); std::size_t seed { h1 + 0x9e3779b9 }; auto h2 = std::hash<decltype(a.GetY())>{}(a.GetY()); seed ^= h2 + 0x9e3779b9 + (seed << 6) + (seed >> 2); return seed; } }; // Non-compliant: string concatenation can potentially throw template <> struct hash<B> { std::size_t operator()(const B& b) const { std::string s{std::to_string(b.GetX()) + ’,’ + std::to_string(b.GetY())}; return std::hash<std::string>{}(s); } }; } int main() { std::unordered_map<A, bool> m1 { { A{5, 7}, true } }; if (m1.count(A{4, 3}) != 0) { // .... } std::unordered_map<B, bool> m2 { { B{5, 7}, true } }; // Lookup can potentially throw if hash function throws if (m2.count(B{4, 3}) != 0) { // .... } }

See also

C++ Core Guidelines [11]: C.89: Make a hash noexcept.