Why is std::hash a struct instead of a function?
Asked Answered
P

2

24

Standard library implements std::hash as a template struct that is specialized for different types. It is used like this:

#include <iostream>
#include <functional>

int main()
{
    std::hash<int> hasher;
    std::cout << hasher(1337) << std::endl;

    return 0;
}

My question is what is the reasoning behind this design choice. Why it isn't implemented as a template function and used like this:

#include <iostream>
#include <functional>

int main()
{
    std::cout << std::hash<int>(1337) << std::endl;

    return 0;
}
Photoreconnaissance answered 24/11, 2013 at 19:20 Comment(3)
the 2 examples are identical, the only difference is that in the second one the object is unnamed .Whyalla
The unordered associate containers have a template type parameter to specify the hash; this allows using stateful hash objects (e.g. using a special value that's XORed on the hash). Getting the type of a function template specialization doesn't have a nice syntax.Blasphemy
@Whyalla The first example compiles, the second does not. The second would need to be written as std::hash<int>()(1337) to use an unnamed temporary struct.Laurielaurier
O
19

There are, multiple reasons, each one good enough to just the choice:

  1. You can partially specialize class templates but you can only fully specialized function templates (at least, so far). Thus, you can provide a replacement for an entire suite of related template arguments with std::hash<T> being a class template. Note that, partial overloading doesn't help because the hash function would need to be specified somehow as an object which can't be done with overloaded functions (unless they are accessed via an object but that's what is differentiated against).
  2. The unordered associative containers are parametersized with a static entity (which can also be customized dynamically if the specific type supports that) which is easier done using class templates.
  3. Since the entities used for the hash function are customizable, the choice is between using a type or a function pointer for customization. Function pointers are often hard to inline while inline member functions of a type are trivial to inline, improving the performance for simple functions like computing a simple hash quite a bit.
Owlet answered 24/11, 2013 at 19:30 Comment(1)
To be fair, the default hash could have called a function which then could for non supported types try calling the .hash() method, which would allow for everything above, give allow for adl hashing (which is better than forcing a std injection), and allow objects to self hash if they want to. But that violates KISS, even though it matches how less and begin work.Blount
M
5

A template function can not be partially specialized for types, while std::hash specialized for different types as a class template.

And, in this template class based way, you can do some meta programming such as accessing to return type and key type like below:

std::hash<X>::argument_type
std::hash<X>::result_type
Mcgraw answered 24/11, 2013 at 19:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.