I'm glad to hear!
Doing things out-of-order -- your 2 questions first.
Why should i make it independent in order to work? |
1. The snarky answer is that it's impossible to use a dependent name in terms of the dependency (well, without some complicated stuff to help, anyways).
The real answer is that
the concept of a hash function is independent of a hash-table. In other words, you can hash something without having a hash-table around.
That's a clear sign that the two concepts should be separated in code. This is because
it's much more difficult to handle two interrelated concepts than two independent concepts.
By defining a hashable object in terms of a hash table, your code made things seem much more complicated than they actually are.
A good guideline is that whenever two concepts can be separated, they should be separated. Do this in your programs.
Another less-general guideline suggests that a nested class should only be used
internally by the class it is defined in terms of. (i.e., the use of a nested class should be an implementation detail). If it needs to be exposed it should be defined outside the class.
2. I used
struct { ... };
for 2 reasons:
-- It's shorter than
class { public: ... };
-- Our
struct Hash
is much closer to a function than an object. Instead it's closer to what category theory calls a
morphism or a
functor. In C++ terms that's a "callable object" or a lambda-expression.
Why's it more a function than an object? Because in the context of the name
struct Hash
, "hash"
is a verb; the object itself does nothing except hash some values. It carries no state and doesn't represent any single hash, it just has a member function that takes a value and hashes it.
Whether or not to use
struct
or
class
is preference, though. I usually write function objects with
struct
.
I didn't use template specializations |
If you didn't use template specializations I'm assuming you wrote a (potentially overloaded) free or member function that takes an instance of the type you need to hash and returns the hash.
Something like
int hash(Student const& s) { ... }
. Please correct me if I'm wrong.
This is fine, but using a template class member function has two main advantages:
1. By making it a class you get the ability to hold state, cleanly -- this might be useful if you have special requirements for a particular hash function.
2. By using specializations instead of function overloads you can create compatible hash objects without ever having to modify the module's code -- that's the Hash class or the Hash table.
Given that template specializations are acceptable, and some class you want to hash like this:
1 2 3 4
|
template<typename T> class Student {
public:
std::string get_name() const;
};
| |
Something like this:
1 2 3 4 5 6 7 8
|
// empty object -- we don't know how to hash everything.
template <typename T> struct Hash { };
// specialization for hashing Student (i.e., hash<Student> can hash a student)
template <> struct Hash<Student> {
// borrowing your hashing algorithm,
// overloading the function-call operator:
unsigned operator() (Student const& s){ return 31 * 17 + (uintptr_t)(&s); }
};
| |
And whenever you need a Student's hash inside HashTable, simply
1 2 3 4 5 6
|
template <typename K, typename V> class HashTable {
public:
int foo(K const& key) const { auto hashCode = hash(key); };
private:
Hash<K> hash;
};
| |
Quick note:
31*17 +(uintptr_t)this->name;
In your hash function this expression has the potential to be overflow when converted to int (which is the return value). If I'm not mistaken, signed integer overflow is undefined behavior. Change the return value to
unsigned int
.