如何覆盖类中定义的枚举的std :: hash?

时间:2015-09-26 17:38:24

标签: c++ c++11 stl unordered-set

我在类中定义了一个枚举类型,我想创建这些对象的unordered_set作为类的成员:

#include <unordered_set>

class Foo {
public:
  enum Bar {
    SOME_VALUE
  };

  // Error: implicit instantiation of std::hash
  std::unordered_set<Bar> getValues() const {
     return _values;
  }

private:
  std::unordered_set<Bar> _values;
};

现在,我知道明显的答案是向unordered_set添加自定义哈希函数:

std::unordered_set<Bar, BarHasher>

然而,我想知道的是,是否有一种方法可以为bar枚举专门化std :: hash,以便任何使用unordered_map的人自动获得散列行为。

这适用于所有其他数据类型,但不适用于枚举 - 因为枚举不能向前声明。

为了使其工作,我必须在枚举定义之后放置std :: hash的定义,但在第一次使用之前,这意味着我必须将它放在类主体的中间,这是行不通的。

3 个答案:

答案 0 :(得分:3)

一种可能性是将枚举放入基类中。不幸的是,您必须为每个枚举成员提供使用声明。一种方法是使用范围枚举( <?php $mysqli = new mysqli("localhost", "my_user", "my_password", "test"); /* check connection */ if ($mysqli->connect_errno) { printf("Connect failed: %s\n", $mysqli->connect_error); exit(); } /* Select queries return a resultset */ if ($result = $mysqli->query("SELECT wie, waar, metwie, voeruig FROM data")) { printf("Select returned %d rows.\n", $result->num_rows); while ($row = mysql_fetch_array($result)) { switch ($row) { case "wie": echo "<select name='wie'>"; echo "<option value='" . $row['wie'] . "'>" . $row['wie'] . "</option>"; echo "</select>"; break; case "waar": echo "<select name='waar'>"; echo "<option value='" . $row['waar'] . "'>" . $row['waar'] . "</option>"; echo "</select>"; break; case "metwie": echo "<select name='metwie'>"; echo "<option value='" . $row['metwie'] . "'>" . $row['metwie'] . "</option>"; echo "</select>"; break; } case "voeruig": echo "<select name='voeruig'>"; echo "<option value='" . $row['voeruig'] . "'>" . $row['voeruig'] . "</option>"; echo "</select>"; break; } /* free result set */ $result->close(); } ?> ),这需要使用enum class Bar而不是Foo::Bar::SOME_VALUE。这样做,您只需要Foo::SOME_VALUE

using FooBase::Bar;

答案 1 :(得分:3)

  

然而,我想知道的是,是否有一种方法可以为bar枚举专门化std :: hash,以便使用unordered_map的任何人自动获得散列行为。

没有奇迹,所以任何人只有在专业化之后才会使用专门的std::hash。由于您无法在另一个类中专门化类并且您的枚举嵌套,因此在类中使用std::hash会有问题。正如你指出的枚举不能向前宣布。因此,在类中使用专门的std::hash是唯一的解决方案(不创建基类或&#34;取消&#34;枚举):通过引用聚合/声明并在std::hash专门化后使用外部。

#include <iostream>
#include <unordered_set>
#include <memory>

struct A {

    enum E {
        first, second
    };

    A();

    std::unique_ptr< std::unordered_set<E> > s_; //!< Here is
};

namespace std {

template<>
class hash<A::E> {
public:
    std::size_t operator()(A::E const& key) const noexcept {
        std::cout << "hash< A::E >::operator()" << std::endl;
        return key;
    }

};

}

A::A()
    : s_(new std::unordered_set<E>)
{ }

int main(void) {
    A a;
    a.s_->insert(A::first);

    std::unordered_set< A::E > s;
    s.insert(A::second);
}

打印

  

散列&LT; A :: E&gt; :: operator()
  散列&LT; A :: E&gt; :: operator()

因此,在课程A之外,每个人都可以将A::Estd::hash以及内部课程一起使用,我们也会将A::Estd::hash一起使用。另外,如果您不想通过引用汇总std::unordered_set,则可以仅为内部使用实现自定义哈希(然后转发std::hash个调用。)

答案 2 :(得分:1)

您似乎已经覆盖了问题中的所有角度。

我无法想办法做到这一点。

总结一下,你只能改变这种情况的事实:

  • 使enum非嵌套(将其放入封闭的命名空间中)或
  • 在示例中明确使用hasher函数。