0

次の問題があり、解決策が見つかりません。もちろん、解決策がまったく存在しない可能性もありますが、あきらめる前に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または少なくとも派生クラスとして持つ必要があることに気付きました。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少なくとも宣言する必要があるため、2 つのコードを交換するだけでは十分ではありません。

エラーログの最初の部分 (非常に長いため):

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

残念ながら、構造体に埋め込まれた列挙型で同様のことを行うことができなかったため、この質問を開始しました。それを解決することは可能ですか、したがって、派生クラスを定義することを避けますか、またはこの場合、派生が唯一の実行可能な解決策ですか?D

4

2 に答える 2

2

ネストされた列挙型を前方宣言することはできません。この回答を参照してください。

ForEveR が説明したように行うことも、std 名前空間に関係なく汎用テンプレートを使用して、データ構造で使用することもできます。これは、ハッシュ関数としてenum_hash使用する必要がないためです。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;
};
于 2015-11-05T14:40:20.923 に答える
1

すべての列挙型に対してハッシュの特殊化を記述するだけで、すべて正常に機能します。

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);
    }
  };
};
于 2015-11-05T14:38:31.393 に答える