12

I thought it would have been, but I can't find this in my standard library implementation (gcc-4.8.2).

Why is std::hash not already specialised for std::reference_wrapper?

#pragma once
#include <functional>

namespace std 
{
    template<typename T>
    struct hash<reference_wrapper<T>>
    {
        size_t operator()(const reference_wrapper<T>& r) const
        {
            return std::hash<T>()(r.get());
        }
    }; 
}
Steve Lorimer
  • 27,059
  • 17
  • 118
  • 213
  • 4
    Perhaps because if you have a container of reference wrappers, it's not obvious whether it should hash the references or the referents, and providing a standard specialization would have to choose one and in doing so confuse people. Since it's not provided, you're forced to make your intent explicit. – Brian Bi Mar 31 '15 at 21:43
  • 1
    If not the reference, what would you hash the `reference_wrapper` on? It's only member is `get()` which returns a `T&` (and the function call operator which does the same). It exists almost solely to allow references to be stored inside standard containers. – Steve Lorimer Mar 31 '15 at 23:24
  • 1
    The pointer, *i.e.*, `std::addressof(r.get())`. – Brian Bi Mar 31 '15 at 23:37
  • But you can just add a hash function which hashes the pointer of the object itself if you want to do that – Steve Lorimer Mar 31 '15 at 23:43
  • 1
    Yes, and you can just add a hash function which hashes the object itself, which is what you've written in your question. But if one or the other were provided in the standard library, it would confuse people expecting the other. Whether they can write the other version themselves is beside the point. – Brian Bi Mar 31 '15 at 23:51
  • I've added a specialisation for `reference_wrapper`. I still need to provide a specialisation for the wrapped type `T`. so I have to provide 2 specialisations. If I want to hash on the pointer, I can specialise hash for my type `T` and do that. If I want to hash on something else, I still need to specialise hash for my type `T` (or provide another hash object). I still don't get what the problem would be having `std::reference_wrapper` pass through to `T` as standard. After all, `hash` is specialised for other wrapper types like `unique_ptr` et al – Steve Lorimer Mar 31 '15 at 23:59
  • 3
    I think the case with smart pointers is very different. You know, a reference is an alias for the object it refers to, and a reference wrapper is a reference-like object. A pointer, on the other hand, is clearly distinct from the object it points to, and a smart pointer is a pointer-like object. It's obvious that a hash specialization for smart pointers *should* hash the address and not the object. For reference wrappers it's not so clear. – Brian Bi Apr 01 '15 at 00:06

1 Answers1

3

std::reference_wrapper is mostly used to provide reference semantics in utilities that default to copying values, such as std::bind.

Direct use of std::reference_wrapper as in a container is essentially like a pointer (except that it is not nullable). Hashing of pointers (and smart pointers) follows reference (i.e. address) semantics.

You can always provide your own hash function, of course. If you define it as a template over all pointers and smart pointers, then T* might be a better choice of value type rather than reference_wrapper<T>.

Note, if you're already hashing objects and storing the hashes, you might eliminate duplicates by keeping everything in an unordered_map. Then value identity and object identity will be the same.

Potatoswatter
  • 134,909
  • 25
  • 265
  • 421