20

Can the C++11 std::hash type be used to hash function pointers? There is a hash partial specialization defined as

template <typename T> struct hash<T*>;

but since function pointers are different from other pointer types in C++ (e.g. they can't be cast to void*), I'm not sure whether it is safe to use it for types like int(*)() or void(*)(int, int).

Is this permitted? Is there any specific wording in the new ISO spec that supports or refutes this?

Thanks!

Marc Mutz - mmutz
  • 24,485
  • 12
  • 80
  • 90
templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
  • Is this simply theoretical? I don't think the default hash function for function pointers has any practical use, since (in most implementations) it simply hashes the integer underlying the pointer. – Siyuan Ren Nov 01 '13 at 03:00
  • @C.R.- It's partially theoretical, though I'm also curious if you could make something like a `std::unordered_map` that could be an inverse map from strings to functions or something of that ilk. – templatetypedef Nov 01 '13 at 03:01
  • I did some tests here and I saw that function pointers successfully cast to `void*`. – Murilo Vasconcelos Nov 01 '13 at 03:10
  • 3
    @MuriloVasconcelos for your particular implementation, yes. But the standard gives no guarantees. – Tristan Brindle Nov 01 '13 at 03:12
  • Although it's strictly not legal, I daresay that it **must** "work" because function pointers to the same function are guaranteed to compare equal, and function pointers to a different function must compare not equal. This somehow implies that every function is a different "thingie", no matter what exactly it is. Thus, if the hash is worth its salt, it should hash to a unique value. – Damon Nov 03 '13 at 22:36
  • @C.R., why is that not practically useful? It means every function will hash to a distinct value, giving a perfect hash. – Jonathan Wakely Nov 03 '13 at 23:16
  • 1
    @JonathanWakely: I was wrong. I thought good hashing must map related inputs to seemingly random outputs, and forgot the ultimate purpose is to avoid collisions. – Siyuan Ren Nov 06 '13 at 04:31

3 Answers3

12

Great question. I don't know the answer for sure, and I'm happy to defer to anyone with better knowledge than me, but my thinking is that even though function pointers aren't the same as data pointers, they are pointers nonetheless: so the std::hash<T*> partial specialisation should be applied.

For what it's worth, the following compiles without warnings even with -pendantic in g++ 4.8.1 and clang 3.3, and works as expected:

#include <functional>
#include <iostream>

void func1(int) {}
void func2(int) {}

int main()
{
    typedef void (*func_type) (int);

    std::hash<func_type> hash;

    std::cout << hash(func1) << std::endl;
    std::cout << hash(func2) << std::endl;

}

I'd be really interested if anyone has any references to the standard to back this up though.

Tristan Brindle
  • 16,281
  • 4
  • 39
  • 82
  • Smart pointers are definitely covered by the standard (\[util.smartptr.hash\] 20.7.2.6). Still looking to see if code pointers are. – WhozCraig Nov 01 '13 at 03:38
  • 10
    Nothing in the standard says `hash` _doesn't_ apply to any pointer type, so it does apply. Specifically, it applies to any "pointers to `void` or objects or functions (including static members of classes) of a given type" (the different types of pointer given in [basic.compound]) – Jonathan Wakely Nov 03 '13 at 23:12
1

I found the following:

17.6.3.4 Hash requirements

A type H meets the Hash requirements if:

  • it is a function object type (20.8)

[...]

And then, the referenced 20.8 states:

A function object type is an object type (3.9) that can be the type of the postfix-expression in a function call (5.2.2, 13.3.1.1).228 A function object is an object of a function object type. In the places where one would expect to pass a pointer to a function to an algorithmic template (Clause 25), the interface is specified to accept a function object. This not only makes algorithmic templates work with pointers to functions, but also enables them to work with arbitrary function objects.

It kind of states it backwards... but the statement not only makes algorithmic templates work with pointers to functions... seems appropriate for your question.

Community
  • 1
  • 1
Escualo
  • 40,844
  • 23
  • 87
  • 135
  • The question does not involve any *non-type* template parameters. `T` is a *type* parameter. – AnT stands with Russia Nov 08 '13 at 04:25
  • @AndreyT Mhhh... I either am misreading or misunderstanding the standard... care to explain? – Escualo Nov 08 '13 at 04:38
  • It is pretty straightforward. There are three kinds of template parameters: #1) **type** parameters, e.g. `template ...`, #2) **non-type** parameters, e.g. `template ...`, #3) **template** parameters, e.g. `template – AnT stands with Russia Nov 08 '13 at 05:48
  • Thanks. I will modify accordingly. – Escualo Nov 08 '13 at 05:53
  • 3
    This paragraph is not about (some made-up) `Hashable`, but about `Hash`. It defines what kinds of types can be used to hash things, not which kinds of things are hashable. The OP asks if some specialization of `std::hash` (which meets the requirements of `Hash`) can be used to hash a function pointer; not if function pointers meet the `Hash` requirements. – dyp Nov 08 '13 at 17:43
-1

It's actually interesting... I bumped into this question while using MSVC++. What I'm trying to do is:

static std::unordered_map<Fun, std::string> FunctionMap()
{
    static std::unordered_map<Fun, std::string> map;
    return map;
}

with Fun a function pointer type.

During compilation, I get the following error:

error C2338: The C++ Standard doesn't provide a hash for this type.
....
_Kty=int (__thiscall Testje::* )(int,int)

In a previous attempt I attempted to cast the function pointer to void*, which isn't allowed and doesn't compile (see: https://isocpp.org/wiki/faq/pointers-to-members#cant-cvt-memfnptr-to-voidptr for details). The reason is that a void* is a data pointer, while a function pointer is a code pointer.

My conclusion so far is that it isn't allowed and it won't compile on MSVC++.

atlaste
  • 30,418
  • 3
  • 57
  • 87
  • 2
    That has nothing to do with it. A *member function pointer* is not a function pointer in the slightest. – Puppy Nov 07 '15 at 20:55