Why is std::hash not specialised for std::reference_wrapper?
Asked Answered
N

1

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());
        }
    }; 
}
Nork answered 31/3, 2015 at 21:35 Comment(7)
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.Futch
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.Nork
The pointer, i.e., std::addressof(r.get()).Futch
But you can just add a hash function which hashes the pointer of the object itself if you want to do thatNork
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.Futch
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 alNork
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.Futch
G
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.

Glennaglennie answered 1/5, 2015 at 6:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.