web-dev-qa-db-ja.com

enumクラスをunordered_mapキーとして使用できません

Enumクラスを含むクラスがあります。

class Shader {
public:
    enum class Type {
        Vertex   = GL_VERTEX_SHADER,
        Geometry = GL_GEOMETRY_SHADER,
        Fragment = GL_FRAGMENT_SHADER
    };
    //...

次に、別のクラスに次のコードを実装すると...

std::unordered_map<Shader::Type, Shader> shaders;

...コンパイルエラーが発生します。

...usr/lib/c++/v1/type_traits:770:38: 
Implicit instantiation of undefined template 'std::__1::hash<Shader::Type>'

ここでエラーの原因は何ですか?

70
Appleshell

ファンクターオブジェクトを使用して、enum classのハッシュを計算します。

struct EnumClassHash
{
    template <typename T>
    std::size_t operator()(T t) const
    {
        return static_cast<std::size_t>(t);
    }
};

これで、std::unordered_mapの3番目のテンプレートパラメーターとして使用できます。

enum class MyEnum {};

std::unordered_map<MyEnum, int, EnumClassHash> myMap;

したがって、std::hashの特殊化を提供する必要はありません。テンプレート引数の演ductionが仕事をします。さらに、Word usingを使用して、EnumClassHashタイプに応じてunordered_mapまたはKeyを使用する独自のstd::hashを作成できます。

template <typename Key>
using HashType = typename std::conditional<std::is_enum<Key>::value, EnumClassHash, std::hash<Key>>::type;

template <typename Key, typename T>
using MyUnorderedMap = std::unordered_map<Key, T, HashType<Key>>;

これで、MyUnorderedMapenum classまたは別のタイプで使用できます。

MyUnorderedMap<int, int> myMap2;
MyUnorderedMap<MyEnum, int> myMap3;

理論的には、HashTypestd::underlying_typeを使用できますが、EnumClassHashは必要ありません。それはこのようなものかもしれませんしかし、私はまだ試していません

template <typename Key>
using HashType = typename std::conditional<std::is_enum<Key>::value, std::hash<std::underlying_type<Key>::type>, std::hash<Key>>::type;

std::underlying_typeの使用が機能する場合、標準の非常に良い提案になる可能性があります。

107
Daniel

これは標準の欠陥と見なされ、C++ 14で修正されました: http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#2148

ただし、gcc 4.9.3の時点では、この解決策はまだlibstdc ++に実装されていません: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=6097

2013年にclangのlibc ++で修正されました: http://lists.cs.uiuc.edu/pipermail/cfe-commits/Week-of-Mon-20130902/087778.html

40
David Stone

非常に簡単な解決策は、次のようなハッシュ関数オブジェクトを提供することです。

std::unordered_map<Shader::Type, Shader, std::hash<int> > shaders;

これが列挙キーのすべてであり、std :: hashの特殊化を提供する必要はありません。

20
denim

KerrekSBが指摘したように、std::hashを使用する場合は、次のようなstd::unordered_mapの特殊化を提供する必要があります。

namespace std
{
    template<>
    struct hash< ::Shader::Type >
    {
        typedef ::Shader::Type argument_type;
        typedef std::underlying_type< argument_type >::type underlying_type;
        typedef std::hash< underlying_type >::result_type result_type;
        result_type operator()( const argument_type& arg ) const
        {
            std::hash< underlying_type > hasher;
            return hasher( static_cast< underlying_type >( arg ) );
        }
    };
}
5
Daniel Frey

std::unordered_mapを使用すると、ハッシュ関数が必要であることがわかります。組み込み型またはSTL型の場合、利用可能なデフォルトがありますが、ユーザー定義のものはありません。地図だけが必要な場合は、std::mapを試してみませんか?

5
CS Pei

これをMyEnumClassを定義するヘッダーに追加します。

namespace std {
  template <> struct hash<MyEnumClass> {
    size_t operator() (const MyEnumClass &t) const { return size_t(t); }
  };
}
4
user3080602