1332. Let Hash objects throw!

Section: 20.5.3.4 [hash.requirements] Status: C++11 Submitter: Daniel Krügler Opened: 2010-03-26 Last modified: 2016-02-10

Priority: Not Prioritized

View all other issues in [hash.requirements].

View all issues with C++11 status.

Discussion:

The currently added Hash requirements demand in Table 40 — Hash requirements [hash]:

Table 40 — Hash requirements [hash]
Expression Return type Requirement
h(k) size_t Shall not throw exceptions. [..]

While it surely is a generally accepted idea that hash function objects should not throw exceptions, this basic constraint for such a fundamental requirement set does neither match the current library policy nor real world cases:

  1. There are little known situations where a swap or move operation may throw an exception and in some popular domains such functions are required not to throw. But the library invested already efforts for good reasons to require "working" container implementations in the presence of throwing move or swap operations, see e.g. 26.2.6.1 [associative.reqmts.except], 26.2.7.1 [unord.req.except].
  2. The container library is already specified to cope with potentially throwing comparers, predicates, and hash function objects, see above.
  3. The new definition goes beyond the original hash requirements as specified by SGI library in regard to the exception requirement:

    http://www.sgi.com/tech/stl/HashFunction.html

  4. There are indeed real-world examples of potentially throwing hash functions, typically when the proxy pattern is used and when the to-be hashed proxied instance is some volatile object, e.g. a file or internet resource, that might suddenly be unavailable at the time of hashing.
  5. With the new noexcept language facility libraries can still take advantage of no-throw guarantees of hasher functions with stricter guarantees.

Even though the majority of all known move, swap, and hash functions won't throw and in some cases must not throw, it seems like unnecessary over-constraining the definition of a Hash functor not to propagate exceptions in any case and it contradicts the general principle of C++ to impose such a requirement for this kind of fundamental requirement.

[ 2010-11-11 Daniel asks the working group whether they would prefer a replacement for the second bullet of the proposed resolution (a result of discussing this with Alberto) of the form: ]

Add to 23.14.15 [unord.hash]/1 a new bullet:

1 The unordered associative containers defined in Clause 23.5 use specializations of the class template hash as the default hash function. For all object types Key for which there exists a specialization hash<Key>, the instantiation hash<Key> shall:

[Batavia: Closed as NAD Future, then reopened. See the wiki for Tuesday.]

Proposed resolution:

  1. Change Table 26 — Hash requirements [tab:hash] as indicated:

    Table 26 — Hash requirements [tab:hash]
    Expression Return type Requirement
    h(k) size_t Shall not throw exceptions. […]
  2. Add to 23.14.15 [unord.hash] p. 1 a new bullet:

    1 The unordered associative containers defined in Clause 26.5 [unord] use specializations of the class template hash as the default hash function. For all object types Key for which there exists a specialization hash<Key>, the instantiation hash<Key> shall:

    • satisfy the Hash requirements ([hash.requirements]), with Key as the function call argument type, the DefaultConstructible requirements (Table [defaultconstructible]), the CopyAssignable requirements (Table [copyassignable]),
    • be swappable ([swappable.requirements]) for lvalues,
    • provide two nested types result_type and argument_type which shall be synonyms for size_t and Key, respectively,
    • satisfy the requirement that if k1 == k2 is true, h(k1) == h(k2) is also true, where h is an object of type hash<Key> and k1 and k2 are objects of type Key,.
    • satisfy the requirement that the expression h(k), where h is an object of type hash<Key> and k is an object of type Key, shall not throw an exception, unless hash<Key> is a user-defined specialization that depends on at least one user-defined type.