正确重载 [括号] 运算符用于哈希表获取和设置

Properly overloading [bracket] operator for hashtable get and set

本文关键字:哈希表 用于 获取 设置 运算符 重载 括号      更新时间:2023-10-16

我正在尝试实现一个哈希表类。我面临的问题是如何正确重载方括号运算符,以便从哈希表中获取键的值与将键设置为值是有区别的。

到目前为止,这是该类的外观:

template <typename K, typename V>
class HashTable {
    typedef pair<K, V> KeyVal;
    avl_tree <KeyVal> **TABLE;
    unsigned TABLESIZE;
    
public:
    HashTable( const unsigned & );
    V& operator [] ( const K& ); //Setter
    const V& operator [](const K&) const; //Getter
    typedef unsigned (*hashtype)(const K&);
    static hashtype Hash;
    ~HashTable();
};

这是括号的每个重载的实现:

template <typename K, typename V>
V& HashTable<K, V>::operator [] ( const K& ret ) {
    unsigned index = HashTable<K, V>::Hash(ret) % TABLESIZE;
    avl_tree <KeyVal> *ptr = AVL_TREE::find(TABLE[index], KeyVal(ret, 0));
    if ( ptr == None ) ptr = (TABLE[index] = AVL_TREE::insert(TABLE[index], KeyVal(ret, 0)));
    return ptr->data.second;
}
template <typename K, typename V>
const V& HashTable<K, V>::operator [](const K& ret) const {
    avl_tree <KeyVal> *ptr = AVL_TREE::find(TABLE[HashTable<K, V>::Hash(ret) % TABLESIZE], KeyVal(ret, 0));
    if (ptr == None) throw "Exception: [KeyError] Key not found exception.";
    return ptr->data.second;
}

现在,如果我这样做:

cout << table["hash"] << "n"; //table declared as type HashTable<std::string, int>

我得到的输出为 0,但我希望它使用重载方括号的 getter 实现;即这应该引发异常。我该怎么做?

处理这种情况的常用方法是让operator[]返回代理。

然后,对于代理重载operator T大致与您在上面完成const重载一样。重载operator=就像您的非常量版本一样。

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();
};

当你使用它时,你基本上有两种情况:

some_hash_table[some_key] = some_value;
value_type v = some_hash_table[some_key];

在这两种情况下,some_hash_table[some_key]都会返回 proxy 的实例。在第一种情况下,您将分配给代理对象,以便调用代理的operator=,将其传递some_value,以便some_valuekey作为其键添加到表中。

在第二种情况下,您尝试将 proxy 类型的对象分配给 value_type 类型的变量。显然,这不能直接赋值 - 但proxy::operator V返回基础Hashtable的值类型的对象 - 因此编译器调用它以生成可以分配给v的值。反过来,它会检查表中是否存在正确的键,如果不存在,则会引发异常。

当成员函数或运算符的等效const和非const重载可用时,在非const实例上调用该方法时,将选择非常量。仅当实例const,或者通过const引用或指针访问实例时,才会选择const重载:

struct Foo
{
  void foo() {}
  void foo() const {}
};
void bar(const Foo& f) { f.foo();}
void baz(const Foo* f) { f->foo(); }
int main()
{
  Foo f;
  f.foo(); // non-const overload chosen
  bar(f);  // const overload chosen
  bar(&f); // const overload chosen
  const Foo cf; // const instance
  cf.foo();     // const overload chosen
  const Foo& rf = f; // const reference
  rf.foo();          // const overload chosen
}