3

C++11 & Boost で次のことを行う簡単な方法はありますか?

  • std::hashから入手可能な場合は常にの標準定義を使用する<functional>
  • が欠落しているがで利用可能な場合boost::hash_valueに定義するために使用します。std::hashstd::hashboost::hash_value<boost/functional/hash.hpp>

例えば:

  • std::hash<std::vector<bool>>標準ライブラリから取得する必要があります。
  • std::hash<std::vector<unsigned>>で実装する必要がありますboost::hash_value
4

2 に答える 2

5

頭に浮かぶ最初のアイデアは、SFINAEを使用し、std::hash<>可能であれば試してみて、それ以外の場合boost::hash_value()は次のように使用することです。

#include <string>
#include <functional>
#include <type_traits>
#include <boost/functional/hash.hpp>

struct my_struct_0 {
    std::string s;
};

template <typename T>
struct has_std_hash_subst { typedef void type; };

template <typename T, typename C = void>
struct has_std_hash : std::false_type {};

template <typename T>
struct has_std_hash<
    T,
    typename has_std_hash_subst<decltype( std::hash<T>()(T()) ) >::type
> : std::true_type {};

template <typename T>
static typename std::enable_if<has_std_hash<T>::value, size_t>::type
make_hash(const T &v)
{
    return std::hash<T>()(v);
}

template <typename T>
static typename std::enable_if<(!has_std_hash<T>::value), size_t>::type
make_hash(const T &v)
{
    return boost::hash_value(v);
}

int main()
{
    make_hash(std::string("Hello, World!"));
    make_hash(my_struct_0({ "Hello, World!" }));
}

std::hash残念ながら、失敗を引き起こすデフォルトの特殊化が常にありますstatic_assert。これは他のライブラリには当てはまらないかもしれませんが、GCC 4.7.2には当てはまります(を参照bits/functional_hash.h:60)。

  /// Primary class template hash.
  template<typename _Tp>
    struct hash : public __hash_base<size_t, _Tp>
    {
      static_assert(sizeof(_Tp) < 0,
                    "std::hash is not specialized for this type");
      size_t operator()(const _Tp&) const noexcept;
    };

したがって、上記のSFINAEアプローチは機能しません—static_assertショーストッパーがあります。したがって、いつ利用可能かを実際に判断することはできませんstd::hash

さて、これは実際にはあなたの質問に答えませんが、便利かもしれません—このトリックを逆に行うことは可能です—最初にBoostの実装をチェックしてから、にフォールバックしてstd::hash<>ください。boost::hash_value()使用可能な場合(つまり、std::stringおよびmy_struct_0)を使用し、そうでない場合std::hash<>(つまり、my_struct_1)を使用する以下の例を検討してください。

#include <string>
#include <functional>
#include <type_traits>
#include <boost/functional/hash.hpp>

struct my_struct_0 {
    std::string s;
};

struct my_struct_1 {
    std::string s;
};

namespace boost {
size_t hash_value(const my_struct_0 &v) {
    return boost::hash_value(v.s);
}
}

namespace std {
template <>
struct hash<my_struct_1> {
    size_t operator()(const my_struct_1 &v) const {
        return std::hash<std::string>()(v.s);
    }
};

}

template <typename T>
struct has_boost_hash_subst { typedef void type; };

template <typename T, typename C = void>
struct has_boost_hash : std::false_type {};

template <typename T>
struct has_boost_hash<
    T,
    typename has_boost_hash_subst<decltype(boost::hash_value(T()))>::type
> : std::true_type {};

template <typename T>
static typename std::enable_if<has_boost_hash<T>::value, size_t>::type
make_hash(const T &v)
{
    size_t ret = boost::hash_value(v);
    std::cout << "boost::hash_value(" << typeid(T).name()
              << ") = " << ret << '\n';
    return ret;
}

template <typename T>
static typename std::enable_if<(!has_boost_hash<T>::value), size_t>::type
make_hash(const T &v)
{
    size_t ret = std::hash<T>()(v);
    std::cout << "std::hash(" << typeid(T).name()
              << ") = " << ret << '\n';
    return ret;
}

int main()
{
    make_hash(std::string("Hello, World!"));
    make_hash(my_struct_0({ "Hello, World!" }));
    make_hash(my_struct_1({ "Hello, World!" }));
}

それが役に立てば幸い。

更新:おそらく、@ ChristianRauによって指摘されたように、ここで説明されているハックを使用して、最初のSFINAEアプローチを機能させることができます!とても汚いですが:)

于 2013-02-14T23:05:36.337 に答える
1

私の答えは正しくないかもしれませんが、答えがノーだと思う理由を説明しようと思います。

std::hash<T>私はそれを交換可能に使用できるとは思わないboost:hash<T>ので、オブジェクトの作成を非表示にして (これが完全な解決策でなくても)、その結果である size_t を返しました。メソッドはもちろんコンパイル時に選択する必要があるため、関数のディスパッチが私の頭に浮かぶものです。サンプルコード:

template <typename T>
size_t createHash(const T& t, false_type)
{
    return boost::hash<T>()(t);
}

template <typename T>
size_t createHash(const T& t, true_type)
{   
    return std::hash<T>()(t);
}

template<typename T>
size_t createHash(const T& t)
{
    return createHash<T>(t, std::is_XXX<T>::type());
}


int main() 
{
    vector<unsigned> v; v.push_back(1);
    auto h1 = createHash(v);
    cout << " hash: " << h1;
    //hash<vector<unsigned> > h2;
}

このコードの考え方は単純です: type の type を構築できる場合はstd::hash<T>2 番目の実装を選択し、そうでない場合は最初の実装を選択します。

最初の実装を選択した場合、コードは問題なくコンパイルされ、fe を使用して確認できます。std::is_array<T>::type()もちろんそうではないラッパー関数で、boost::hash 実装が選択されます。ただし、 fe のようtrue_tに aを返すトレイトを使用すると、次に、コンパイラは「C++ 標準は提供していません...」と報告します。vector<unsigned>std::is_class<T>::type()static_assert

true_tこれが機能するためには、型が本当に構築可能である場合 (static_assert が失敗しない場合) とそうでない場合は、コンパイラに強制的に返す必要がfalse_tあります。ただし、その可能性はないと思います。

于 2013-02-13T23:04:02.457 に答える