2016-07-23 5 views
0

Я не профессионал в C++, но каким-то образом я предоставил решение при переносе моего кода MSVS 2015 C++ в MinGW 4.9.2, чтобы специализировать класс std::hash для поддержки всех enum. Здесь есть какие-либо разработчики компилятора C++ или прокси-программисты C++, можете ли вы объяснить, почему эта специализация работает, хотя это неопределенное поведение в соответствии со стандартом C++, который они говорят?Почему этот код предоставляет специализацию для ** ALL ** перечислений для std :: hash template?

Link for full code with samples on Gist

#include <unordered_set> 
#include <functional> 

#if (defined __GNUC__) && (__GNUC__ < 6) 
// Adds support of std::hash<enum T> to libstdc++. 
// GCC 6 provides it out of the box. 
namespace std { 
    template<typename T> 
    struct hash { 
     constexpr size_t operator()(typename std::enable_if<std::is_enum<T>::value, T>::type s) const noexcept { 
      return static_cast<size_t>(s); 
     } 
    }; 
} 
#endif 

Обеспечение поддержки std::hash<enum T> означает, что все классы, как std::unordered_XXX будет поддерживать любой enum в качестве ключа.

GCC 6.1.0 с это std::hash определение с ошибкой

error: redefinition of 'struct std::hash<_Tp>' 

GCC 5.3.0 без это std::hash определение не выполняется для станд :: unordered_set с следующей ошибкой:

In file included from /usr/local/gcc-5.3.0/include/c++/5.3.0/bits/hashtable.h:35:0, 
       from /usr/local/gcc-5.3.0/include/c++/5.3.0/unordered_set:47, 
       from prog.cc:1: 
/usr/local/gcc-5.3.0/include/c++/5.3.0/bits/hashtable_policy.h: In instantiation of 'struct std::__detail::__is_noexcept_hash<Foo, std::hash<Foo> >': 
/usr/local/gcc-5.3.0/include/c++/5.3.0/type_traits:137:12: required from 'struct std::__and_<std::__is_fast_hash<std::hash<Foo> >, std::__detail::__is_noexcept_hash<Foo, std::hash<Foo> > >' 
/usr/local/gcc-5.3.0/include/c++/5.3.0/type_traits:148:38: required from 'struct std::__not_<std::__and_<std::__is_fast_hash<std::hash<Foo> >, std::__detail::__is_noexcept_hash<Foo, std::hash<Foo> > > >' 
/usr/local/gcc-5.3.0/include/c++/5.3.0/bits/unordered_set.h:95:63: required from 'class std::unordered_set<Foo>' 
prog.cc:25:25: required from here 
/usr/local/gcc-5.3.0/include/c++/5.3.0/bits/hashtable_policy.h:85:34: error: no match for call to '(const std::hash<Foo>) (const Foo&)' 
    noexcept(declval<const _Hash&>()(declval<const _Key&>()))> 
           ^
In file included from /usr/local/gcc-5.3.0/include/c++/5.3.0/bits/move.h:57:0, 
       from /usr/local/gcc-5.3.0/include/c++/5.3.0/bits/stl_pair.h:59, 
       from /usr/local/gcc-5.3.0/include/c++/5.3.0/utility:70, 
       from /usr/local/gcc-5.3.0/include/c++/5.3.0/unordered_set:38, 
       from prog.cc:1: 
/usr/local/gcc-5.3.0/include/c++/5.3.0/type_traits: In instantiation of 'struct std::__not_<std::__and_<std::__is_fast_hash<std::hash<Foo> >, std::__detail::__is_noexcept_hash<Foo, std::hash<Foo> > > >': 
/usr/local/gcc-5.3.0/include/c++/5.3.0/bits/unordered_set.h:95:63: required from 'class std::unordered_set<Foo>' 
prog.cc:25:25: required from here 
/usr/local/gcc-5.3.0/include/c++/5.3.0/type_traits:148:38: error: 'value' is not a member of 'std::__and_<std::__is_fast_hash<std::hash<Foo> >, std::__detail::__is_noexcept_hash<Foo, std::hash<Foo> > >' 
    : public integral_constant<bool, !_Pp::value> 
... 

ответ

4

Это плохо сформированная программа, не требующая диагностики, потому что не разрешается предоставлять базовые специализации для типов шаблонов в std.

Это работало, потому что в некоторых реализациях предоставление такой базовой специализации происходит с перенаправлением std::hash<T> без явной специализации на реализацию хеша. Затем ваш operator() работает только с enum с, но это не то, почему он работает, и не мешает ему сделать вашу программу плохо сформированной без необходимости диагностики.

Практически это возможно сломалось, потому что кто-то SFINAE включил std::hash с пустой базой в gcc 6.1: это может быть задано некоторым стандартом C++, неуверенным, но это качество улучшения реализации, если нет.

Правильный способ сделать это состоит в создании

struct enum_hash { 
    template<typename T> 
    constexpr 
    typename std::enable_if<std::is_enum<T>::value,std::size_t>::type 
    operator()(T s) const noexcept { 
    return static_cast<std::size_t>(s); 
    } 
}; 

Какого типа, который может хэширование любого перечисления.

Теперь перейдите ,enum_hash в unordered_set<some_enum, enum_hash> вот так.

+0

похоже, что есть только шаблон struct hash; в GCC <6.1.0. Я имею в виду, что для этого шаблона нет тела, и это означает, что я предоставляю тело своим кодом, я прав? – slavanap

+0

@slav да. Он по-прежнему является плохо сформированной программой без необходимости диагностики, но компилятор генерирует программу без сообщения об ошибке, и эта программа (поведение которой больше не регулируется стандартом) ведет себя так, чтобы соответствовать тому, что вы хотите, из-за реализация причуд. В принципе, делать это не стоит, когда стоимость, которую он экономит, заключается в добавлении ', enum_hash' к неупорядоченным картам/наборам. – Yakk

 Смежные вопросы

  • Нет связанных вопросов^_^