0

私は C++ のプロではありませんが、MSVS 2015 C++ コードを MinGW 4.9.2 に移植して、std::hashすべての をサポートするようにクラスを特殊化する際に、何らかの方法で解決策を提供しましたenum。ここに C++ コンパイラの開発者または C++ プロ プログラマーがいる場合、C++ 標準によると未定義の動作であるにもかかわらず、この特殊化が機能する理由を説明できますか?

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がキーとして any をサポートすることを意味しenumます。

この定義のGCC 6.1.0std::hashエラーで失敗します

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

この定義のないGCC 5.3.0std::hashは、std::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

1 に答える 1

5

これは、診断の必要のない不適切な形式のプログラムです。これは、 でテンプレート タイプの基本的な特殊化を提供することが許可されていないためですstd

一部の実装では、このような基本的な特殊化を提供すると、明示的な特殊化なしですべて std::hash<T>がハッシュ実装にリダイレクトされるため、機能しました。次に、 s でoperator()のみ動作しますenumが、それが機能する理由ではなく、診断を必要とせずにプログラムが不正になるのを防ぐものでもありません。

実際には、誰かが SFINAE がstd::hashgcc 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>ます。

于 2016-07-23T00:51:58.957 に答える