【问题标题】:User-defined hash function for unordered_mapunordered_map 的用户定义哈希函数
【发布时间】:2017-05-12 07:40:17
【问题描述】:

我正在尝试在 stl unordered_map 上创建一个模板化包装类。我将散列函数类作为模板参数传递给包装类,并提供了字符串特化。下面的代码编译并工作,但如果包含注释部分,则编译错误:

"/usr/include/c++/6/bits/unordered_map.h:143:28: 错误:没有匹配 调用‘HashFunction

的函数

::HashFunction()' const hasher& __hf = hasher(),".

但是,我一定有散列函数类的ctor。我尝试了各种方法,但无法使其工作。请提供您的想法/cmets。

#include <iostream>
#include <unordered_map>
using namespace std;

template< class Key >
class HashFunction
{
  public:
    //HashFunction( const Key & inKey );
    size_t operator()(const Key &inKey) const;
  private:
    unsigned mHashCode;
};

//template<>
//HashFunction< string >::HashFunction( const string & inKey )
//{
    //mHashCode=std::hash<string>{}(inKey);
//}

template <> 
size_t HashFunction< string >::operator()(const string &inKey) const 
{
  return std::hash<string>{}(inKey);
  //return mHashCode;
}

template< class Key, class Val, class Hash = HashFunction< Key > >
class unordered_map_wrapper
{
  public:
   unordered_map_wrapper();
  private:
    unordered_map<Key, Val, Hash> * mTable;
};

template< class Key, class Val, class Hash >
unordered_map_wrapper< Key, Val, Hash >::unordered_map_wrapper()
{
    mTable=new unordered_map<Key, Val, Hash>(10);
}

int main() {
    unordered_map_wrapper<string, unsigned> h;
    return 0;
}

【问题讨论】:

  • 注释部分缺少 const 说明符,即使您在类声明中声明它也是如此。
  • 为标准已经提供的字符串创建哈希函数的任何原因
  • 我也无法理解这种语法 std::hash{}(inKey);请解释
  • 由于这是类构造函数的问题,请查看The Rule of 3, 5 &amp; 0

标签: c++ hash stl unordered-map


【解决方案1】:

这不是哈希模板参数在std::unordered_map 中的工作方式!该映射创建一个单独的 Hash 实例(使用默认构造函数,因此您需要提供它!),该实例用于进一步需要的所有哈希计算。所以你的类必须看起来像这样:

template<class Key>
class HashFunction
{
public:
    // HashFunction(); <- can just leave it out...
    size_t operator()(const Key& inKey) const;
};

template<>
size_t HashFunction<std::string>::operator()(const std::string& inKey) const
{
    // calculate hash value in operator!!!
    return std::hash<std::string>()(inKey);
}

不过,为了避免一直构造 std::hash,我宁愿专门化整个模板类:

template<class Key>
class HashFunction; // leave entirely unimplemented
                    // (if it has no general meaning, at least...)

template<>
class HashFunction<std::string>
{
public:
    size_t operator()(const std::string& inKey) const
    {
        return mHash(inKey);
    }
private:
    std::hash<std::string> mHash;
};

不过,重新实现已经存在的东西没有多大意义。我可以假设您在这里使用 std::string 作为您自己班级的占位符吗?

顺便说一句:一个简单的 typedef 比你的包装类更容易完成这项工作:

template <typename Key, typename Value>
using my_unordered_map = std::unordered_map<Key, Value, HashFunction<Key>>;

也许您也对替代品感兴趣?我们首先假设你有两个类(而不是 std::string):

class C1
{ };
class C2
{ };

最优雅的解决方案(至少在我看来)只是为你的类专门化 std::hash (虽然一般来说,向 std 命名空间添加一些东西是非法的,模板专门化是例外......):

namespace std
{
template<>
class hash<C1>
{
public:
    size_t operator()(C const& c) const
    {
        return 0; // your implementation here...
    }
};
// same for C2
}

// no typedef necessary, just use std::unordered_map directly:
std::unordered_map<C1, unsigned> m1;
std::unordered_map<C2, unsigned> m2;

您可以提供自己的 HashFunction 类,提供重载

class HashFunction
{
public:
    size_t operator()(C1 const& c) const
    {
        return 0;
    }
    size_t operator()(C2 const& c) const
    {
        return 0;
    }
};

同样,typedef 让您的生活更轻松:

template <typename Key, typename Value>
using my_unordered_map = std::unordered_map<Key, Value, HashFunction>;

std::unordered_map<C1, unsigned, HashFunction> m1;
my_unordered_map<C1, unsigned> m1_;

std::unordered_map<C2, unsigned, HashFunction> m2;
my_unordered_map<C2, unsigned> m2_;

最后,如果您绝对需要使用一些参数初始化您的 HashFunction 以正确配置它,您可以这样做,但您必须交付一个预配置的实例然后到你的哈希图!

template<typename T>
class HashFunction
{
public:
    HashFunction(double value);
    size_t operator()(T const& c) const;
private:
    double data;
};

template<typename T>
HashFunction<T>::HashFunction(double value)
        : data(value)
{ }

template<>
size_t HashFunction<C1>::operator()(C1 const& c) const
{
    return static_cast<size_t>(data);
};
template<>
size_t HashFunction<C2>::operator()(C2 const& c) const
{
    return static_cast<size_t>(data);
};

template <typename Key, typename Value>
using my_unordered_map = std::unordered_map<Key, Value, HashFunction<Key>>;

my_unordered_map<C1, unsigned> m1(12, HashFunction<C1>(10.12));
my_unordered_map<C2, unsigned> m2(10, HashFunction<C2>(12.10));

好吧,现在,至少,你的包装类可以再次派上用场了:

template <typename Key, typename Value, typename Hash = HashFunction<Key>>
class unordered_map_wrapper
{
public:
    unordered_map_wrapper()
        : mMap(16, Hash())
    { }
    unordered_map_wrapper(double parameter)
        : mMap(16, Hash(parameter))
    { }
private:
    std::unordered_map<Key, Value, Hash> mMap;
};

unordered_map_wrapper<C1, unsigned> m1(10.12);
unordered_map_wrapper<C2, unsigned> m2(12.10);
// possible due to provided default ctor:
unordered_map_wrapper<std::string, unsigned, std::hash<std::string>> m3;

【讨论】:

    【解决方案2】:

    它期望有一个默认构造函数,当你定义你的自定义版本时它会被移除。

    这样编译:

    template< class Key >
    class HashFunction
    {
    public:
        HashFunction(){};
        HashFunction( const Key & inKey );
        size_t operator()(const Key &inKey) const;
    private:
        unsigned mHashCode;
    };
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-11-05
      • 1970-01-01
      • 1970-01-01
      • 2020-08-23
      • 1970-01-01
      • 1970-01-01
      • 2020-11-12
      • 2016-06-17
      相关资源
      最近更新 更多