Using hash_range with polymorphism
I'm having a hard time reconciling two distinct notions. Suppose I have a class hierarchy with root class Foo, and subclasses Foo1, Foo2, etc. (all of which are leaf classes) Now, suppose that hash_value() is defined for all the subclasses but not Foo itself (say, if Foo was abstract). Now suppose I have this: struct FooList { std::vector<boost::shared_ptr<Foo>> foos; } std::size_t hash_value(const FooList& list) { std::size_t seed = 0; typedef boost::indirect_iterator<std::vector<boost::shared_ptr<Foo>>::const_iterator> Iterator; boost::hash_range(seed, Iterator(list.foos.begin()), Iterator(list.foos.end()); return seed; } This seems a bit incorrect (and it would fail to compile due to "ambiguous overload" of hash_combine or something). What I want to do is something along the lines of std::size_t hash_value(const FooList& list) { std::size_t seed = 0; for (std::vector<boost::shared_ptr<Foo>>::const_iterator it = list.foos.begin(); it != list.foos.end(); ++it) { boost::hash_combine(seed, **it); } return seed; } That is, hash the Foos pointed to by the foos (using the correct derived class hash_value() functions) and not the shared_ptrs themselves. Is there a "shorter" version of the latter (which I presume is correct), or will the latter have to do?
On 1 December 2011 07:48, Kelvin Chung <kelvSYC@mac.com> wrote:
I'm having a hard time reconciling two distinct notions. Suppose I have a class hierarchy with root class Foo, and subclasses Foo1, Foo2, etc. (all of which are leaf classes) Now, suppose that hash_value() is defined for all the subclasses but not Foo itself (say, if Foo was abstract).
Now suppose I have this:
struct FooList { std::vector<boost::shared_ptr<Foo>> foos; }
std::size_t hash_value(const FooList& list)
You should probably be including something representing the dynamic type in the hash value, but that depends on how you implement 'operator==' for FooList. If you don't then defining 'hash_value' doesn't make much sense.
On 2011-12-01 14:05:07 +0000, Daniel James said:
On 1 December 2011 07:48, Kelvin Chung <kelvSYC@mac.com> wrote:
I'm having a hard time reconciling two distinct notions. Suppose I have a class hierarchy with root class Foo, and subclasses Foo1, Foo2, etc. (all of which are leaf classes) Now, suppose that hash_value() is defined for all the subclasses but not Foo itself (say, if Foo was abstract).
Now suppose I have this:
struct FooList { std::vector<boost::shared_ptr<Foo>> foos; }
std::size_t hash_value(const FooList& list)
You should probably be including something representing the dynamic type in the hash value, but that depends on how you implement 'operator==' for FooList. If you don't then defining 'hash_value' doesn't make much sense.
That's what I am trying to do: I'm trying to use the hash_value() for the actual type of the Foos in foos (eg. go through foos, and call hash_value(const Foo1&) if the pointer in foos is a pointer to a Foo1 upcasted to Foo, and so on), but all I am apparently doing is slicing down to Foo, and as hash_value(const Foo&) does not exist, I get a compile-time error. FooList::operator==() is fairly straightforward, I'd imagine: iterate through the two foos and call a polymorphic equals() function to determine equality. I could do a polymorphic Foo::hash() and then redirect hash_value() for the leaf classes to hash(): class Foo { protected: virtual std::size_t hash() = 0; }; std::size_t Foo1::hash() { … } // public in Foo1 // It would be nice to have a template that does this so I don't have to do this for every leaf class std::size_t hash_value(const Foo1& foo1) { return foo1.hash(); } But it does seem like reinventing the wheel. If this is in fact the preferred solution to the conundrum,
Kelvin Chung wrote:
// It would be nice to have a template that does this so I don't have to do this for every leaf class std::size_t hash_value(const Foo1& foo1) { return foo1.hash(); }
You don't need to define hash_value for the leaf classes, only for the base class. std::size_t hash_value(const Foo& foo) { return foo.hash(); }
On 1 December 2011 14:05, Daniel James <dnljms@gmail.com> wrote:
You should probably be including something representing the dynamic type in the hash value, but that depends on how you implement 'operator==' for FooList. If you don't then defining 'hash_value' doesn't make much sense.
Sorry, that wasn't clear. I meant, "If you don't define operator== then defining hash_value doesn't make much sense". This is because 'boost::hash' implements a hash value for the equivalence relation defined by 'std::equal_to'. You need to establish what 'operator==' does in order to know what 'hash_value' should do.
participants (3)
-
Daniel James
-
Kelvin Chung
-
Peter Dimov