简体   繁体   English

内部作用域枚举,哈希函数和unordered_set数据成员

[英]Inner scoped enumeration, hash function and unordered_set data member

I've the following problem of which I cannot find a solution. 我遇到以下问题,但找不到解决方案。 Of course, it could be that a solution does not exist at all, but I'd like to have a try on SO before to give up. 当然,可能根本就没有解决方案,但是我想在放弃之前先尝试一下SO。

First of all, a snippet that compiles with no errors: 首先,一个没有错误编译的代码段:

#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;
}

With this code in mind, I found myself with the requirement of having the unordered_set as a data member of S or, at least, a derived class. 考虑到此代码,我发现自己需要将unordered_set作为S的数据成员或至少是派生类。 A possible working solution is to add add the following lines once the std namespace has been closed: 一个可行的解决方案是在关闭std命名空间后添加以下行:

struct D: public S {
    std::unordered_set<S::E> set;
};

Another possible solution is maybe (I've not tried it) to use an unscoped enumeration. 另一个可能的解决方法是(我没有尝试过)使用无范围的枚举。 Anyway, the first attempt I made was to modify the definition of the struct S as it follows: 无论如何,我的第一个尝试是修改struct S的定义,如下所示:

struct S {
    enum class E: unsigned int { FOO = 0, BAR };
    std::unordered_set<E> set;
};

This ends in an error because (if I've correctly understood the problem) the unordered_set requires the specialized hash function. 由于(如果我已经正确理解了问题) unordered_set需要专用的hash函数,因此这将导致错误结束。 Anyway, the latter requires S::E to be at least declared, thus it is not enough to swap the two pieces of code. 无论如何,后者要求至少声明S::E ,因此仅交换这两个代码是不够的。

Here the first part of the error log (for it's very long): 这是错误日志的第一部分(因为它很长):

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;

Usually, in such a case, I can solve with something like a forward declaration, as the one in the example below: 通常,在这种情况下,我可以使用前向声明之类的方法来解决,如下面的示例中所示:

struct B;
struct A { B *link; };
struct B { A *link; };

Unfortunately, I've not been able to do something similar with the enum embedded in a struct and that's why I started this question. 不幸的是,我无法对嵌入在结构体中枚举做类似的事情,这就是为什么我开始这个问题。 Is it possible to solve it, thus avoid to define the derived class D , or deriving is the only viable solution in this case? 有可能解决它,从而避免定义派生类D ,或者在这种情况下派生是唯一可行的解​​决方案?

You can't forward declare a nested enum, see this answer. 你不能向前声明嵌套枚举,看到这个答案。

You can do as ForEveR explained, or you can have your generic enum_hash template regardless of std namespace and use it in your data structure, since you are not forced to use std::hash as the hashing function, eg: 您可以按照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;
};

You can just write specialization of hash for all enums and then all would work fine. 您可以为所有枚举编写哈希的特殊化,然后一切正常。

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);
    }
  };
};

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM