頭に浮かぶ最初のアイデアは、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アプローチを機能させることができます!とても汚いですが:)