有一种简单的方法可以使用C ++ 11&升压:
std::hash
<functional>
的标准定义
boost::hash_value
缺少std::hash
但std::hash
中有boost::hash_value
的情况下,使用<boost/functional/hash.hpp>
来定义std::hash<std::vector<bool>>
。例如:
std::hash<std::vector<unsigned>>
应来自标准库boost::hash_value
应与{{1}}实施。答案 0 :(得分: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指出的here描述的黑客攻击,并使第一个SFINAE方法工作!虽然它非常脏:)
答案 1 :(得分: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;
}
这段代码的想法很简单:如果你可以构造类型std::hash<T>
的类型,选择第二种实现,如果没有 - 选择第一种。
如果选择了第一个实现,代码编译没有问题,您可以使用fe进行检查。包装函数中的std::is_array<T>::type()
,当然不是真的,因此将选择boost :: hash实现。但是,如果您使用的特性会为true_t
返回vector<unsigned>
,就像fe一样。 std::is_class<T>::type()
然后编译器将报告“C ++标准不提供...”,这是static_assert
的结果。
为了实现这一点,我们需要强制编译器返回true_t
如果一个类型真的是可构造的(它不会使static_assert失败),如果不是,则需要false_t
。{{1}}。但是,我认为没有可能这样做。