为什么此代码为 std::hash 模板的 **ALL** 枚举提供专业化?
Why this code provides specialization for **ALL** enums for std::hash template?
我不是 C++ 专家,但我在将我的 MSVS 2015 C++ 代码移植到 MinGW 4.9.2 以专门化 std::hash
class 以支持所有 enum
s。如果这里有任何 C++ 编译器开发人员或 C++ 专业程序员,你能解释为什么这个专业化有效吗,尽管根据他们所说的 C++ 标准,它是未定义的行为?
Link for full code with samples on Gist
#include <unordered_set>
#include <functional>
#if (defined __GNUC__) && (__GNUC__ < 6)
// Adds support of std::hash<enum T> to libstdc++.
// GCC 6 provides it out of the box.
namespace std {
template<typename T>
struct hash {
constexpr size_t operator()(typename std::enable_if<std::is_enum<T>::value, T>::type s) const noexcept {
return static_cast<size_t>(s);
}
};
}
#endif
提供对 std::hash<enum T>
的支持意味着所有像 std::unordered_XXX
这样的 class 都将支持任何 enum
作为键。
GCC 6.1.0 with 此 std::hash
定义失败并出现错误
error: redefinition of 'struct std::hash<_Tp>'
GCC 5.3.0 without 此 std::hash
定义对于 std::unordered_set 失败并出现以下错误:
In file included from /usr/local/gcc-5.3.0/include/c++/5.3.0/bits/hashtable.h:35:0,
from /usr/local/gcc-5.3.0/include/c++/5.3.0/unordered_set:47,
from prog.cc:1:
/usr/local/gcc-5.3.0/include/c++/5.3.0/bits/hashtable_policy.h: In instantiation of 'struct std::__detail::__is_noexcept_hash<Foo, std::hash<Foo> >':
/usr/local/gcc-5.3.0/include/c++/5.3.0/type_traits:137:12: required from 'struct std::__and_<std::__is_fast_hash<std::hash<Foo> >, std::__detail::__is_noexcept_hash<Foo, std::hash<Foo> > >'
/usr/local/gcc-5.3.0/include/c++/5.3.0/type_traits:148:38: required from 'struct std::__not_<std::__and_<std::__is_fast_hash<std::hash<Foo> >, std::__detail::__is_noexcept_hash<Foo, std::hash<Foo> > > >'
/usr/local/gcc-5.3.0/include/c++/5.3.0/bits/unordered_set.h:95:63: required from 'class std::unordered_set<Foo>'
prog.cc:25:25: required from here
/usr/local/gcc-5.3.0/include/c++/5.3.0/bits/hashtable_policy.h:85:34: error: no match for call to '(const std::hash<Foo>) (const Foo&)'
noexcept(declval<const _Hash&>()(declval<const _Key&>()))>
^
In file included from /usr/local/gcc-5.3.0/include/c++/5.3.0/bits/move.h:57:0,
from /usr/local/gcc-5.3.0/include/c++/5.3.0/bits/stl_pair.h:59,
from /usr/local/gcc-5.3.0/include/c++/5.3.0/utility:70,
from /usr/local/gcc-5.3.0/include/c++/5.3.0/unordered_set:38,
from prog.cc:1:
/usr/local/gcc-5.3.0/include/c++/5.3.0/type_traits: In instantiation of 'struct std::__not_<std::__and_<std::__is_fast_hash<std::hash<Foo> >, std::__detail::__is_noexcept_hash<Foo, std::hash<Foo> > > >':
/usr/local/gcc-5.3.0/include/c++/5.3.0/bits/unordered_set.h:95:63: required from 'class std::unordered_set<Foo>'
prog.cc:25:25: required from here
/usr/local/gcc-5.3.0/include/c++/5.3.0/type_traits:148:38: error: 'value' is not a member of 'std::__and_<std::__is_fast_hash<std::hash<Foo> >, std::__detail::__is_noexcept_hash<Foo, std::hash<Foo> > >'
: public integral_constant<bool, !_Pp::value>
...
这是一个格式错误的程序,不需要诊断,因为不允许为 std
中的模板类型提供基本专业化。
之所以有效,是因为在某些实现中,提供这样的基础专业化恰好重定向 all std::hash<T>
而无需对哈希实现进行显式专业化。然后你的 operator()
继续只与 enum
s 一起工作,但这不是它工作的原因,也不会阻止它使你的程序格式错误而无需诊断。
实际上,它可能会崩溃,因为有人 SFINAE 在 gcc 6.1 中启用了 std::hash
一个空的基本实现:这可能是某些 C++ 标准强制要求的,不确定,但如果不是的话,这是实现质量的改进。
正确的方法是创建
struct enum_hash {
template<typename T>
constexpr
typename std::enable_if<std::is_enum<T>::value,std::size_t>::type
operator()(T s) const noexcept {
return static_cast<std::size_t>(s);
}
};
这是一种可以散列任何枚举的类型。
现在像那样将 ,enum_hash
传递给 unordered_set<some_enum, enum_hash>
。
我不是 C++ 专家,但我在将我的 MSVS 2015 C++ 代码移植到 MinGW 4.9.2 以专门化 std::hash
class 以支持所有 enum
s。如果这里有任何 C++ 编译器开发人员或 C++ 专业程序员,你能解释为什么这个专业化有效吗,尽管根据他们所说的 C++ 标准,它是未定义的行为?
Link for full code with samples on Gist
#include <unordered_set>
#include <functional>
#if (defined __GNUC__) && (__GNUC__ < 6)
// Adds support of std::hash<enum T> to libstdc++.
// GCC 6 provides it out of the box.
namespace std {
template<typename T>
struct hash {
constexpr size_t operator()(typename std::enable_if<std::is_enum<T>::value, T>::type s) const noexcept {
return static_cast<size_t>(s);
}
};
}
#endif
提供对 std::hash<enum T>
的支持意味着所有像 std::unordered_XXX
这样的 class 都将支持任何 enum
作为键。
GCC 6.1.0 with 此 std::hash
定义失败并出现错误
error: redefinition of 'struct std::hash<_Tp>'
GCC 5.3.0 without 此 std::hash
定义对于 std::unordered_set 失败并出现以下错误:
In file included from /usr/local/gcc-5.3.0/include/c++/5.3.0/bits/hashtable.h:35:0,
from /usr/local/gcc-5.3.0/include/c++/5.3.0/unordered_set:47,
from prog.cc:1:
/usr/local/gcc-5.3.0/include/c++/5.3.0/bits/hashtable_policy.h: In instantiation of 'struct std::__detail::__is_noexcept_hash<Foo, std::hash<Foo> >':
/usr/local/gcc-5.3.0/include/c++/5.3.0/type_traits:137:12: required from 'struct std::__and_<std::__is_fast_hash<std::hash<Foo> >, std::__detail::__is_noexcept_hash<Foo, std::hash<Foo> > >'
/usr/local/gcc-5.3.0/include/c++/5.3.0/type_traits:148:38: required from 'struct std::__not_<std::__and_<std::__is_fast_hash<std::hash<Foo> >, std::__detail::__is_noexcept_hash<Foo, std::hash<Foo> > > >'
/usr/local/gcc-5.3.0/include/c++/5.3.0/bits/unordered_set.h:95:63: required from 'class std::unordered_set<Foo>'
prog.cc:25:25: required from here
/usr/local/gcc-5.3.0/include/c++/5.3.0/bits/hashtable_policy.h:85:34: error: no match for call to '(const std::hash<Foo>) (const Foo&)'
noexcept(declval<const _Hash&>()(declval<const _Key&>()))>
^
In file included from /usr/local/gcc-5.3.0/include/c++/5.3.0/bits/move.h:57:0,
from /usr/local/gcc-5.3.0/include/c++/5.3.0/bits/stl_pair.h:59,
from /usr/local/gcc-5.3.0/include/c++/5.3.0/utility:70,
from /usr/local/gcc-5.3.0/include/c++/5.3.0/unordered_set:38,
from prog.cc:1:
/usr/local/gcc-5.3.0/include/c++/5.3.0/type_traits: In instantiation of 'struct std::__not_<std::__and_<std::__is_fast_hash<std::hash<Foo> >, std::__detail::__is_noexcept_hash<Foo, std::hash<Foo> > > >':
/usr/local/gcc-5.3.0/include/c++/5.3.0/bits/unordered_set.h:95:63: required from 'class std::unordered_set<Foo>'
prog.cc:25:25: required from here
/usr/local/gcc-5.3.0/include/c++/5.3.0/type_traits:148:38: error: 'value' is not a member of 'std::__and_<std::__is_fast_hash<std::hash<Foo> >, std::__detail::__is_noexcept_hash<Foo, std::hash<Foo> > >'
: public integral_constant<bool, !_Pp::value>
...
这是一个格式错误的程序,不需要诊断,因为不允许为 std
中的模板类型提供基本专业化。
之所以有效,是因为在某些实现中,提供这样的基础专业化恰好重定向 all std::hash<T>
而无需对哈希实现进行显式专业化。然后你的 operator()
继续只与 enum
s 一起工作,但这不是它工作的原因,也不会阻止它使你的程序格式错误而无需诊断。
实际上,它可能会崩溃,因为有人 SFINAE 在 gcc 6.1 中启用了 std::hash
一个空的基本实现:这可能是某些 C++ 标准强制要求的,不确定,但如果不是的话,这是实现质量的改进。
正确的方法是创建
struct enum_hash {
template<typename T>
constexpr
typename std::enable_if<std::is_enum<T>::value,std::size_t>::type
operator()(T s) const noexcept {
return static_cast<std::size_t>(s);
}
};
这是一种可以散列任何枚举的类型。
现在像那样将 ,enum_hash
传递给 unordered_set<some_enum, enum_hash>
。