Re: [boost] Re: [review] hash functions

In-Reply-To: <d1409a$kc1$1@sea.gmane.org> daniel@calamity.org.uk (Daniel James) wrote (abridged):
I imagine you would use a hasher which dereferenced the pointer. template<typename T> struct hash_ptr { size_t operator()(const T *p) const { return hash_value( *p ); } }; And perhaps this should be included in boost too? Eg this: unordered_set< shared_ptr<MyType>, hash_ptr<MyType> > looks reasonable to me.
Absolutely. In general when writing hash_value() you need to look at the corresponding equality operator. For example: bool Derived::operator==( const Base &base ) const { if (Base::operator==( base )) if (const Derived *p = dynamic_cast( const Derived * >(&base)) if (m_value == p->m_value) return true; return false; } Ideally we look at this and we decide there ought to be something in the hash_value which corresponds to the dynamic_cast. But what? We can try using hash_value( typeid(*this) ). However, that will be relatively slow and may not give good results, depending on how it is implemented. We could use our own type IDs, but allocating them is tricky. So it is tempting not to bother, and instead use: size_t Derived::hash_value() const { // return combine_hash( Base::hash_value(), m_value ); size_t hash = Base::hash_value(); combine_hash( hash, m_value ); return hash; } which is correct, but will produce more collisions than if we'd used the typeid. If hash_value( m_value ) yielded different results for different types of value, then the number of collisions would be reduced. The stability requirement compromises the quality of hashing. -- Dave Harris, Nottingham, UK

"Dave Harris" <brangdon@cix.compulink.co.uk> wrote in message news:memo.518849@cix.compulink.co.uk... | In-Reply-To: <d1409a$kc1$1@sea.gmane.org> | daniel@calamity.org.uk (Daniel James) wrote (abridged): | > If Thorsten writes ptr_unordered_set<base> then there might be a | > problem with boost::hash<base>. Although it will equally apply to | > std::equal_to<base>. | | Absolutely. In general when writing hash_value() you need to look at the | corresponding equality operator. For example: | | bool Derived::operator==( const Base &base ) const { | if (Base::operator==( base )) | if (const Derived *p = dynamic_cast( const Derived * >(&base)) | if (m_value == p->m_value) | return true; | return false; | } | | Ideally we look at this and we decide there ought to be something in the | hash_value which corresponds to the dynamic_cast. But what? are you saying we don't want to hash a Base* as a void*, but use object properties instead? -Thorsten

Dave Harris wrote:
You also need equal_ptr<MyType>. Also, it is easy to break the above unordered_set my modifying some of the MyTypes.
size_t Derived::hash_value() const { size_t seed = my_unique_value; hash_combine( seed, Base::hash_value() ); hash_combine( seed, m_value ); return seed; }
participants (3)
-
brangdon@cix.compulink.co.uk
-
Peter Dimov
-
Thorsten Ottosen