我们什么时候应该为`std :: unordered_set`提供我们自己的Hash函数

时间:2013-07-17 16:33:03

标签: c++ c++11 stl

当我编译以下代码时,我看到了与Hash相关的错误。

int F_no_meaningA(unordered_set<vector<int>>& setVec, vector<int>& vec) 
{
    setVec.insert(vec);
    return 1;
}

int main()
{
  vector<int> W{2, 3, 7}; 
  unordered_set<vector<int>> setVec; 
}

$ g++ --version
g++ (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3

$ g++ $1.cpp -o $1 -g -Wall -Weffc++ -pedantic -std=c++0x
  

/tmp/ccCQFQ4N.o:在功能上   `的std :: __详细:: _ Hash_code_base

     
    

,std :: vector&gt ;,std :: _ Identity&gt; &gt;中     std :: equal_to&gt; &gt;中     std :: hash&gt; &gt;中     的std :: __细节:: _ Mod_range_hashing,     的std :: __细节:: _ Default_ranged_hash,     false&gt; :: _ M_hash_code(std :: vector&gt; const&amp;)     const':/ usr /include/c++/4.6/bits/hashtable_policy.h:753:undefined     对std::hash<std::vector<int, std::allocator<int> > ::operator()(std::vector<int, std::allocator<int> >) const' /tmp/ccCQFQ4N.o: In function std :: __ detail :: _ Hash_code_base的引用     ,std :: vector&gt ;,std :: _ Identity&gt; &gt;中     std :: equal_to&gt; &gt;中     std :: hash&gt; &gt;中     的std :: __细节:: _ Mod_range_hashing,     的std :: __细节:: _ Default_ranged_hash,     false&gt; :: _ M_bucket_index(std :: __ detail :: _ Hash_node&gt;,false&gt; const *,unsigned int)const':     /usr/include/c++/4.6/bits/hashtable_policy.h:763:未定义的引用     到`std :: hash&gt;     :: operator()(std :: vector&gt;)const'colle2:ld返回1退出状态

  

然后,我介绍了以下自己的Hash,问题就解决了。

问题1 &gt;我们什么时候应该为std::unordered_set提供自己的哈希?  我们何时应该为std::unordered_set提供我们自己的等效函数?

struct HashVector : unary_function<vector<int>, vector<int>::size_type> {
  vector<int>::size_type operator()(const vector<int>& vec) const {
    vector<int>::size_type sum = 0;
    for(int i : vec) {
      sum = sum*37 + hash<int>()(i);
    }
    return sum;
  }
};

int F_no_meaningB(unordered_set<vector<int>, HashVector>& setVec, vector<int>& vec) 
{
    setVec.insert(vec);
    return 1;
}

int main()
{
  vector<int> W{2, 3, 7}; 
  unordered_set<vector<int>, HashVector> setVec; 
}
  

警告:基类'struct std :: unary_function,   unsigned int&gt;'有一个非虚析构函数[-Weffc ++]

问题2 &gt;为什么g ++抱怨带有上述警告的结构HashVector?

谢谢

2 个答案:

答案 0 :(得分:6)

  

我们应该何时为std::unordered_set提供自己的哈希值?

当您使用没有标准库提供的哈希的类型时。例如,它不为标准容器提供哈希函数,包括vector<int>

  

为什么g ++抱怨带有上述警告的struct HashVector?

因为您曾使用-Weffc++请求(略微过度热心)警告,以便在您从没有虚拟析构函数的类继承时告诉您。对于大多数继承用途(即多态性),您不希望这样做。但是,在这种情况下,继承只是使用(或者,有些人可能会说,滥用)将一些定义注入到类中,因此警告并不表示存在问题。

不推荐像std::unary_function这样的类,所以最好的解决办法就是不要继承它。

答案 1 :(得分:5)

  

我们什么时候应该为std :: unordered_set提供我们自己的Hash?

标准只需要有限数量的专业化,主要用于原始类型。这是因为这些原始类型具有一些合理的默认“一刀切”散列函数,实现可以提供。更复杂的类型(例如自定义类型或容器)没有明显甚至合理的默认哈希值,因此,您需要提供自己的类型或容器。如果不支持您的value-type,则必须为其提供哈希函数实现。

此外,提供自己的哈希函数的另一个原因是,当您对unordered_set中值的分布有一些额外的专业知识时。散列表的性能对散列函数相对于存储在表中的值的分布的适当性非常敏感。 Here是一个更完整的解释。标准默认值只是一个通用的解决方案,这意味着它既简单又方便,但几乎总是次优。

  

为什么g ++抱怨带有上述警告的struct HashVector?

这主要是应用警告,这些警告主要与经典的面向对象编程相关(使用基类作为派生类的动态多态接口)。在这种上下文中,将析构函数定义为虚拟(这允许从基类实例(例如delete base_ptr;)正确销毁派生类是一个非常严重的错误。正如迈克建议的那样,这是因为-Weffc++已启用(主要应用新手级别的经典OOP样式警告消息)。但是,在代码中,继承用于泛型编程的上下文中,其中继承使用的是非常不同的方式(主要是为了让一个类具有一些基础工程属性和特性)。在这种情况下,基类没有虚拟析构函数不是问题,因为它不打算用在中动态多态设置,而不是静态多态设置。

另请注意,std::unary_function(及其亲属)已在最新标准(C ++ 11)中弃用。这是因为最新标准(使用<type_traits>decltype和类型推断)提供的内省类型增强功能。

相关问题