2015-11-05 8 views
0

У меня есть следующая проблема, из-за которой я не могу найти решение. Конечно, может быть, что решение вообще не существует, но я бы хотел попробовать, прежде чем сдаться.Внутренняя область перечисления, функция хэша и член данных unordered_set

Прежде всего, фрагмент кода, который компилируется без ошибок:

#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, поэтому его недостаточно для замены двух частей кода.

Здесь первая часть журнала ошибок (поскольку это очень долго):

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, или получение в этом случае единственного жизнеспособного решения?

+0

'enum class S_E ...; struct S {typedef S_E E;}; 'Всегда используйте' S :: E' почти везде, но 'S_E', где вам нужно работать с круговой зависимостью. –

ответ

2

Вы не можете вперед объявить вложенное перечисление см this ответа.

Вы можете сделать, как НАВСЕГДА объяснил, или вы можете иметь свой общий 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; 
}; 
+0

Итак, вы говорите, что на самом деле нет возможности разбить цикл, но используя шаблон или все-в-одну специализацию, такую ​​как предложенная ForEveR? Это имеет смысл, я думал то же самое, но мне было любопытно, был ли сложный способ, используя форвардные объявления. – skypjack

+0

Да, текущий стандарт этого не позволяет, см. Http://stackoverflow.com/questions/27019292/is-in-class-enum-forward-declaration-possible – Jack

+0

Хорошо, не нашел этот вопрос раньше, извините. Вы ответили хорошим решением и хорошей ссылкой, действительно спасибо!Не могли бы вы поместить эту ссылку в ответ, заявив, что не возможно иметь прямое объявление такого типа, а затем предлагать свое решение ниже? Это поможет будущему поиску или новичкам. Я приму ваш ответ, потому что он более полный. Еще раз спасибо. – skypjack

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