2015-08-28 1 views
2

Я хочу использовать unordered_map<std::pair<enum_class,other_enum_class>,std::uint8_t> для управления некоторыми форматами пикселей.Недопустимое использование неполного типа struct std :: hash с unordered_map с std :: pair класса enum как ключ

Здесь минимальный код:

#include <unordered_map> 
#include <utility> 
#include <cstdint> 
#include <iostream> 
#include <functional> 

enum class PNM : std::uint8_t { PBM, PGM, PPM }; 
enum class Format : bool  { BIN, ASCII }; 

struct pair_hash { 
public: 
    template <typename T, typename U> 
    std::size_t operator()(const std::pair<T, U> &x) const { 
     return std::hash<T>()(x.first)^std::hash<U>()(x.second); 
    } 
}; 

int main(){ 

    std::unordered_map<std::pair<PNM, Format>, std::uint8_t, pair_hash> k_magic_number ({ 
     { { PNM::PBM, Format::BIN }, 1 }, { { PNM::PGM, Format::BIN }, 2 }, { { PNM::PPM, Format::BIN }, 3 }, 
     { { PNM::PBM, Format::ASCII }, 4 }, { { PNM::PGM, Format::ASCII }, 5 }, { { PNM::PPM, Format::ASCII }, 6 } 
    }); 

    std::cout << k_magic_number[std::make_pair<PNM, Format>(PNM::PBM, Format::BIN)]; 
} 

С GCC У меня есть error, когда я пытаюсь создать экземпляр класса:

main.cpp:14:24: error: invalid use of incomplete type 'struct std::hash'
return std::hash()(x.first)^std::hash()(x.second);
In file included from
/usr/local/include/c++/5.2.0/bits/basic_string.h:5469:0,
from /usr/local/include/c++/5.2.0/string:52,
[...]

С Clang У меня также есть error:

error: implicit instantiation of undefined template 'std::hash' return std::hash()(x.first)^std::hash()(x.second); /usr/local/bin/../lib/gcc/x86_64-unknown-linux-gnu/5.2.0/../../../../include/c++/5.2.0/bits/hashtable_policy.h:1257:16: note: in instantiation of function template specialization 'pair_hash::operator()' requested here [...]

С VS2013 У меня нет ошибки и код компилируется и выполняется.

Что не хватает в моем коде?

+0

В зависимости от вашей потребности вы также можете рассмотреть использование std :: map, в котором отсутствует требование хэширования. – JDseca

ответ

5

г ++ - 5 дает следующие ошибки:

invalid use of incomplete type struct std::hash<PNM>

invalid use of incomplete type struct std::hash<Format>

Таким образом, вы просто должны специализироваться std::hash для PNM и Format.

namespace std { 
template<> 
struct hash<PNM> 
{ 
    typedef PNR argument_type; 
    typedef size_t result_type; 

    result_type operator() (const argument_type& x) const 
    { 
     using type = typename std::underlying_type<argument_type>::type; 
     return std::hash<type>()(static_cast<type>(x)); 
    } 
}; 

template<> 
struct hash<Format> 
{ 
    typedef Format argument_type; 
    typedef size_t result_type;  

    result_type operator() (const argument_type& x) const 
    { 
     using type = typename std::underlying_type<argument_type>::type; 
     return std::hash<type>()(static_cast<type>(x)); 
    } 
}; 

} 

Или вы можете написать шаблон-структуру, которая будет работать только для enums с использованием SFINAE (не уверен, что это не UB стандартом, так как это не специализация на самом деле).

namespace std 
{ 

template<typename E> 
struct hash 
{ 
    typedef E argument_type; 
    typedef size_t result_type; 
    using sfinae = typename std::enable_if<std::is_enum<E>::value>::type; 

    result_type operator() (const E& e) const 
    { 
     using base_t = typename std::underlying_type<E>::type; 
     return std::hash<base_t>()(static_cast<base_t>(e)); 
    } 
}; 

} 
+0

Спасибо, я попробую, чтобы у них не было специализации, поскольку в основном есть bools и целые числа? – coincoin

+2

@coincoin они являются «перечисляемыми классами», это отличается от int. – ForEveR

+0

Спасибо @ForEveR это [компилирует и выполняет] (http://coliru.stacked-crooked.com/a/42a6f4e7505f70a1). Но почему мой 'std :: cout' не печатается? – coincoin