The usual way to handle this situation is to have operator[]
return a proxy.
Then, for the proxy overload operator T
approximately as you've done your const
overload above. Overload operator=
about like your non-const version.
template <typename K, typename V>
class HashTable {
typedef pair<K, V> KeyVal;
avl_tree <KeyVal> **TABLE;
unsigned TABLESIZE;
template <class K, class V>
class proxy {
HashTable<K, V> &h;
K key;
public:
proxy(HashTable<K, V> &h, K key) : h(h), key(key) {}
operator V() const {
auto pos = h.find(key);
if (pos) return *pos;
else throw not_present();
}
proxy &operator=(V const &value) {
h.set(key, value);
return *this;
}
};
public:
HashTable( const unsigned & );
proxy operator [] ( const K& k) { return proxy(*this, k); }
typedef unsigned (*hashtype)(const K&);
static hashtype Hash;
~HashTable();
};
You basically have two cases when you use this:
some_hash_table[some_key] = some_value;
value_type v = some_hash_table[some_key];
In both cases, some_hash_table[some_key]
returns an instance of proxy
. In the first case, you're assigning to the proxy object, so that invokes the proxy's operator=
, passing it some_value
, so some_value
gets added to the table with key
as its key.
In the second case, you're trying to assign an object of type proxy
to a variable of type value_type
. Obviously that can't be assigned directly -- but proxy::operator V
returns an object of the value type for the underlying Hashtable
-- so the compiler invokes that to produce a value that can be assigned to v
. That, in turn, checks for the presence of the proper key in the table, and throws an exception if its not present.