0

我不是 C++ 专家,但不知何故,我在将我的 MSVS 2015 C++ 代码移植到 MinGW 4.9.2 以专门std::hash支持所有enums 类时提供了一个解决方案。这里有任何 C++ 编译器开发人员或 C++ 专业程序员,您能解释一下为什么这种专业化有效,尽管根据他们所说的 C++ 标准它是未定义的行为吗?

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将支持任何enum作为键。

具有此定义的GCC 6.1.0std::hash失败并出现错误

error: redefinition of 'struct std::hash<_Tp>'

没有此定义的GCC 5.3.0std::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>
...
4

1 回答 1

5

这是一个格式错误的程序,不需要诊断,因为不允许在std.

它之所以有效,是因为在某些实现中,提供这样的基本特化恰好将所有 std::hash<T>没有明确特化的东西重定向到您的哈希实现。然后您operator()继续只与enums 一起工作,但这不是它工作的原因,也不会阻止它使您的程序格式不正确而无需诊断。

实际上,它可能崩溃了,因为有人std::hash在 gcc 6.1 中启用了一个空的基本实现 SFINAE:这可能是某些 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>

于 2016-07-23T00:51:58.957 回答