内部作用域枚举、散列函数和 unordered_set 数据成员
Inner scoped enumeration, hash function and unordered_set data member
我有以下问题,但我找不到解决方案。
当然,也有可能根本就没有解决方案,但我还是想尝试一下SO再放弃。
首先,编译没有错误的片段:
#include <unordered_set>
#include <memory>
struct S {
enum class E: unsigned int { FOO = 0, BAR };
};
namespace std
{
template<>
struct hash<S::E> {
using argument_type = S::E;
using underlying_type = std::underlying_type<argument_type>::type;
using result_type = std::size_t;
result_type operator()(argument_type const &s) const noexcept {
const underlying_type us = static_cast<underlying_type>(s);
hash<underlying_type> hfn;
return hfn(us);
}
};
}
int main() {
std::unordered_set<S::E> set;
}
考虑到这段代码,我发现自己需要将 unordered_set
作为 S
的数据成员,或者至少是派生的 class。一个可行的解决方案是在 std
命名空间关闭后添加以下行:
struct D: public S {
std::unordered_set<S::E> set;
};
另一种可能的解决方案是(我还没有尝试过)使用无范围枚举。无论如何,我所做的第一次尝试是修改 struct S
的定义,如下所示:
struct S {
enum class E: unsigned int { FOO = 0, BAR };
std::unordered_set<E> set;
};
这以错误结束,因为(如果我正确理解了问题的话)unordered_set
需要专门的 hash
函数。反正后者需要至少声明S::E
,所以交换两段代码是不够的。
这是错误日志的第一部分(因为它很长):
In file included from /usr/include/c++/5/bits/hashtable.h:35:0,
from /usr/include/c++/5/unordered_set:47,
from main.cpp:1:
/usr/include/c++/5/bits/hashtable_policy.h: In instantiation of ‘struct std::__detail::__is_noexcept_hash<S::E, std::hash<S::E> >’:
/usr/include/c++/5/type_traits:137:12: required from ‘struct std::__and_<std::__is_fast_hash<std::hash<S::E> >, std::__detail::__is_noexcept_hash<S::E, std::hash<S::E> > >’
/usr/include/c++/5/type_traits:148:38: required from ‘struct std::__not_<std::__and_<std::__is_fast_hash<std::hash<S::E> >, std::__detail::__is_noexcept_hash<S::E, std::hash<S::E> > > >’
/usr/include/c++/5/bits/unordered_set.h:95:63: required from ‘class std::unordered_set<S::E>’
main.cpp:6:27: required from here
/usr/include/c++/5/bits/hashtable_policy.h:85:34: error: no match for call to ‘(const std::hash<S::E>) (const S::E&)’
noexcept(declval<const _Hash&>()(declval<const _Key&>()))>
^
In file included from /usr/include/c++/5/bits/move.h:57:0,
from /usr/include/c++/5/bits/stl_pair.h:59,
from /usr/include/c++/5/utility:70,
from /usr/include/c++/5/unordered_set:38,
from main.cpp:1:
/usr/include/c++/5/type_traits: In instantiation of ‘struct std::__not_<std::__and_<std::__is_fast_hash<std::hash<S::E> >, std::__detail::__is_noexcept_hash<S::E, std::hash<S::E> > > >’:
/usr/include/c++/5/bits/unordered_set.h:95:63: required from ‘class std::unordered_set<S::E>’
main.cpp:6:27: required from here
/usr/include/c++/5/type_traits:148:38: error: ‘value’ is not a member of ‘std::__and_<std::__is_fast_hash<std::hash<S::E> >, std::__detail::__is_noexcept_hash<S::E, std::hash<S::E> > >’
: public integral_constant<bool, !_Pp::value>
^
In file included from /usr/include/c++/5/unordered_set:48:0,
from main.cpp:1:
/usr/include/c++/5/bits/unordered_set.h: In instantiation of ‘class std::unordered_set<S::E>’:
main.cpp:6:27: required from here
/usr/include/c++/5/bits/unordered_set.h:95:63: error: ‘value’ is not a member of ‘std::__not_<std::__and_<std::__is_fast_hash<std::hash<S::E> >, std::__detail::__is_noexcept_hash<S::E, std::hash<S::E> > > >’
typedef __uset_hashtable<_Value, _Hash, _Pred, _Alloc> _Hashtable;
^
/usr/include/c++/5/bits/unordered_set.h:102:45: error: ‘value’ is not a member of ‘std::__not_<std::__and_<std::__is_fast_hash<std::hash<S::E> >, std::__detail::__is_noexcept_hash<S::E, std::hash<S::E> > > >’
typedef typename _Hashtable::key_type key_type;
通常,在这种情况下,我可以使用前向声明之类的东西来解决,如下例所示:
struct B;
struct A { B *link; };
struct B { A *link; };
不幸的是,我无法使用嵌入在 struct 中的 enum 做类似的事情,这就是我开始这个的原因题。是否可以解决它,从而避免定义派生class D
,或者在这种情况下派生是唯一可行的解决方案?
您可以只为所有枚举编写散列特化,然后一切正常。
namespace std {
template<class E>class hash {
using sfinae = typename std::enable_if<std::is_enum<E>::value, E>::type;
public:
size_t operator()(const E&e) const {
return std::hash<typename std::underlying_type<E>::type>()(e);
}
};
};
您不能转发声明嵌套枚举,请参阅 this 答案。
您可以按照 ForEveR 的说明进行操作,或者您可以使用通用的 enum_hash
模板而不考虑 std 命名空间,并在您的数据结构中使用它,因为您不必使用 std::hash
作为哈希函数,例如:
template<typename T>
struct enum_hash {
using argument_type = T;
using underlying_type = typename std::underlying_type<argument_type>::type;
using result_type = std::size_t;
result_type operator()(argument_type const &s) const noexcept {
const underlying_type us = static_cast<underlying_type>(s);
std::hash<underlying_type> hfn;
return hfn(us);
}
static_assert(std::is_enum<T>::value, "T must be an enum!");
};
struct S {
enum class E: unsigned int { FOO = 0, BAR };
std::unordered_set<S::E, enum_hash<S::E>> set;
};
我有以下问题,但我找不到解决方案。 当然,也有可能根本就没有解决方案,但我还是想尝试一下SO再放弃。
首先,编译没有错误的片段:
#include <unordered_set>
#include <memory>
struct S {
enum class E: unsigned int { FOO = 0, BAR };
};
namespace std
{
template<>
struct hash<S::E> {
using argument_type = S::E;
using underlying_type = std::underlying_type<argument_type>::type;
using result_type = std::size_t;
result_type operator()(argument_type const &s) const noexcept {
const underlying_type us = static_cast<underlying_type>(s);
hash<underlying_type> hfn;
return hfn(us);
}
};
}
int main() {
std::unordered_set<S::E> set;
}
考虑到这段代码,我发现自己需要将 unordered_set
作为 S
的数据成员,或者至少是派生的 class。一个可行的解决方案是在 std
命名空间关闭后添加以下行:
struct D: public S {
std::unordered_set<S::E> set;
};
另一种可能的解决方案是(我还没有尝试过)使用无范围枚举。无论如何,我所做的第一次尝试是修改 struct S
的定义,如下所示:
struct S {
enum class E: unsigned int { FOO = 0, BAR };
std::unordered_set<E> set;
};
这以错误结束,因为(如果我正确理解了问题的话)unordered_set
需要专门的 hash
函数。反正后者需要至少声明S::E
,所以交换两段代码是不够的。
这是错误日志的第一部分(因为它很长):
In file included from /usr/include/c++/5/bits/hashtable.h:35:0,
from /usr/include/c++/5/unordered_set:47,
from main.cpp:1:
/usr/include/c++/5/bits/hashtable_policy.h: In instantiation of ‘struct std::__detail::__is_noexcept_hash<S::E, std::hash<S::E> >’:
/usr/include/c++/5/type_traits:137:12: required from ‘struct std::__and_<std::__is_fast_hash<std::hash<S::E> >, std::__detail::__is_noexcept_hash<S::E, std::hash<S::E> > >’
/usr/include/c++/5/type_traits:148:38: required from ‘struct std::__not_<std::__and_<std::__is_fast_hash<std::hash<S::E> >, std::__detail::__is_noexcept_hash<S::E, std::hash<S::E> > > >’
/usr/include/c++/5/bits/unordered_set.h:95:63: required from ‘class std::unordered_set<S::E>’
main.cpp:6:27: required from here
/usr/include/c++/5/bits/hashtable_policy.h:85:34: error: no match for call to ‘(const std::hash<S::E>) (const S::E&)’
noexcept(declval<const _Hash&>()(declval<const _Key&>()))>
^
In file included from /usr/include/c++/5/bits/move.h:57:0,
from /usr/include/c++/5/bits/stl_pair.h:59,
from /usr/include/c++/5/utility:70,
from /usr/include/c++/5/unordered_set:38,
from main.cpp:1:
/usr/include/c++/5/type_traits: In instantiation of ‘struct std::__not_<std::__and_<std::__is_fast_hash<std::hash<S::E> >, std::__detail::__is_noexcept_hash<S::E, std::hash<S::E> > > >’:
/usr/include/c++/5/bits/unordered_set.h:95:63: required from ‘class std::unordered_set<S::E>’
main.cpp:6:27: required from here
/usr/include/c++/5/type_traits:148:38: error: ‘value’ is not a member of ‘std::__and_<std::__is_fast_hash<std::hash<S::E> >, std::__detail::__is_noexcept_hash<S::E, std::hash<S::E> > >’
: public integral_constant<bool, !_Pp::value>
^
In file included from /usr/include/c++/5/unordered_set:48:0,
from main.cpp:1:
/usr/include/c++/5/bits/unordered_set.h: In instantiation of ‘class std::unordered_set<S::E>’:
main.cpp:6:27: required from here
/usr/include/c++/5/bits/unordered_set.h:95:63: error: ‘value’ is not a member of ‘std::__not_<std::__and_<std::__is_fast_hash<std::hash<S::E> >, std::__detail::__is_noexcept_hash<S::E, std::hash<S::E> > > >’
typedef __uset_hashtable<_Value, _Hash, _Pred, _Alloc> _Hashtable;
^
/usr/include/c++/5/bits/unordered_set.h:102:45: error: ‘value’ is not a member of ‘std::__not_<std::__and_<std::__is_fast_hash<std::hash<S::E> >, std::__detail::__is_noexcept_hash<S::E, std::hash<S::E> > > >’
typedef typename _Hashtable::key_type key_type;
通常,在这种情况下,我可以使用前向声明之类的东西来解决,如下例所示:
struct B;
struct A { B *link; };
struct B { A *link; };
不幸的是,我无法使用嵌入在 struct 中的 enum 做类似的事情,这就是我开始这个的原因题。是否可以解决它,从而避免定义派生class D
,或者在这种情况下派生是唯一可行的解决方案?
您可以只为所有枚举编写散列特化,然后一切正常。
namespace std {
template<class E>class hash {
using sfinae = typename std::enable_if<std::is_enum<E>::value, E>::type;
public:
size_t operator()(const E&e) const {
return std::hash<typename std::underlying_type<E>::type>()(e);
}
};
};
您不能转发声明嵌套枚举,请参阅 this 答案。
您可以按照 ForEveR 的说明进行操作,或者您可以使用通用的 enum_hash
模板而不考虑 std 命名空间,并在您的数据结构中使用它,因为您不必使用 std::hash
作为哈希函数,例如:
template<typename T>
struct enum_hash {
using argument_type = T;
using underlying_type = typename std::underlying_type<argument_type>::type;
using result_type = std::size_t;
result_type operator()(argument_type const &s) const noexcept {
const underlying_type us = static_cast<underlying_type>(s);
std::hash<underlying_type> hfn;
return hfn(us);
}
static_assert(std::is_enum<T>::value, "T must be an enum!");
};
struct S {
enum class E: unsigned int { FOO = 0, BAR };
std::unordered_set<S::E, enum_hash<S::E>> set;
};