使用boost :: hash_value在C++ 11中定义std :: hash

Juk*_*ela 2 c++ hash boost c++11 stdhash

有没有一种简单的方法可以使用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.

小智 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!" }));
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,总是有一个默认的特化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;
    };
Run Code Online (Sandbox Code Playgroud)

所以上面的SFINAE方法不起作用 - static_assert有一个显示阻止.因此,您无法确定何时std::hash可用.

现在,这并没有真正回答你的问题,但可能会派上用场 - 反过来可以做这个技巧 - 首先检查Boost实现,然后才回过头来std::hash<>.考虑以下示例,boost::hash_value()如果它可用(即for std::stringmy_struct_0)以及其他方式使用std::hash<>(即for 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!" }));
}
Run Code Online (Sandbox Code Playgroud)

希望能帮助到你.

更新:也许您可以使用@ChristianRau指出的这里描述的黑客,并使第一个SFINAE方法工作!虽然很脏:)