如何为 class 中定义的枚举覆盖 std::hash?
How to override std::hash for an enum defined inside a class?
我在 class 中定义了一个枚举类型,我想创建一个 unordered_set 这些对象作为 class 的成员:
#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 的定义放在枚举定义之后,但在第一次使用之前,这意味着我必须将它放在class 正文,这是行不通的。
您似乎涵盖了问题中的所有角度。
我想不出办法。
回顾一下,只能改变事实:
- 使
enum
成为非嵌套的(改为将其放在封闭的命名空间中),或者
- 在您的示例中明确使用散列函数。
一种可能是将枚举放入基数 class。不幸的是,您必须为每个枚举成员提供一个 using 声明。一种解决方法是使用范围枚举 (enum class Bar
),这需要使用 Foo::Bar::SOME_VALUE
而不是 Foo::SOME_VALUE
。这样做,您只需要 using FooBase::Bar;
.
class FooBase {
public:
enum Bar {
SOME_VALUE
};
protected:
~FooBase() = default; //so can't be used polymorphically
};
//hash goes here
class Foo : FooBase {
public:
using FooBase::Bar;
using FooBase::SOME_VALUE;
...
However, what I'm wondering is if there's a way to specialize std::hash for the Bar enum so that anyone who uses unordered_map gets the hashing behavior automatically.
没有奇迹,所以任何人都会在其专业化后使用专业化std::hash
。因为你不能在另一个 class 中专门化 classes 并且你的枚举是嵌套的,所以在 class 中使用 std::hash
会有问题。正如您指出的那样,枚举不能向前声明。因此,唯一的解决方案(不创建基础 classes 或 "unnesting" 枚举)在 class 中使用专门的 std::hash
:通过引用聚合/声明并在 [ 之后使用外部=11=]专业化。
#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);
}
打印出来
hash< A::E >::operator()
hash< A::E >::operator()
因此,在 class A
之外每个人都可以使用 A::E
和 std::hash
以及在 class 内部我们也使用 A::E
与 std::hash
。此外,如果您不想通过引用聚合 std::unordered_set
,您可以实现自定义散列器仅供内部使用(然后将 std::hash
调用转发给它)。
我在 class 中定义了一个枚举类型,我想创建一个 unordered_set 这些对象作为 class 的成员:
#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 的定义放在枚举定义之后,但在第一次使用之前,这意味着我必须将它放在class 正文,这是行不通的。
您似乎涵盖了问题中的所有角度。
我想不出办法。
回顾一下,只能改变事实:
- 使
enum
成为非嵌套的(改为将其放在封闭的命名空间中),或者 - 在您的示例中明确使用散列函数。
一种可能是将枚举放入基数 class。不幸的是,您必须为每个枚举成员提供一个 using 声明。一种解决方法是使用范围枚举 (enum class Bar
),这需要使用 Foo::Bar::SOME_VALUE
而不是 Foo::SOME_VALUE
。这样做,您只需要 using FooBase::Bar;
.
class FooBase {
public:
enum Bar {
SOME_VALUE
};
protected:
~FooBase() = default; //so can't be used polymorphically
};
//hash goes here
class Foo : FooBase {
public:
using FooBase::Bar;
using FooBase::SOME_VALUE;
...
However, what I'm wondering is if there's a way to specialize std::hash for the Bar enum so that anyone who uses unordered_map gets the hashing behavior automatically.
没有奇迹,所以任何人都会在其专业化后使用专业化std::hash
。因为你不能在另一个 class 中专门化 classes 并且你的枚举是嵌套的,所以在 class 中使用 std::hash
会有问题。正如您指出的那样,枚举不能向前声明。因此,唯一的解决方案(不创建基础 classes 或 "unnesting" 枚举)在 class 中使用专门的 std::hash
:通过引用聚合/声明并在 [ 之后使用外部=11=]专业化。
#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);
}
打印出来
hash< A::E >::operator()
hash< A::E >::operator()
因此,在 class A
之外每个人都可以使用 A::E
和 std::hash
以及在 class 内部我们也使用 A::E
与 std::hash
。此外,如果您不想通过引用聚合 std::unordered_set
,您可以实现自定义散列器仅供内部使用(然后将 std::hash
调用转发给它)。