简体   繁体   English

自定义类的unordered_map插入相同的键时不会导致错误

[英]unordered_map for custom class does not cause error when inserting the same key

I'm trying to figure out some points on using unordered_map for custom class. 我试图弄清楚对自定义类使用unordered_map几点。 Below are the codes I use to take exercise where I define a simple class Line . 下面是我用来定义简单Line类的代码。 I'm confused that why the insertion of Line2 in main() does not make the program outputs insert failed when the value of m for Line1 and Line2 are both 3 . 我感到困惑的是,当Line1Line2m值均为3时,为什么在main()中插入Line2不会使程序输出insert failed Note since I only compare the first value (ie m ) in the operator== function in class Line , thus Line1 and Line2 in this code should have the same key. 注意,由于我只比较class Line operator==函数中的第一个值(即m ),因此此代码中的Line1Line2应该具有相同的键。 Shouldn't the insertion of an already existed key be invalid? 插入已经存在的键不应该是无效的吗? Could some one explain why to me? 有人可以向我解释为什么吗? Thanks! 谢谢!

#include<iostream>                                                                                                                                                                                                                                                                                                                                                                                                                                          
#include<unordered_map>                                                                                                                                                                                                                    

using namespace std;                                                                                                                                                                                                                       
class Line {                                                                                                                                                                                                                               
public:                                                                                                                                                                                                                                    
  float m;                                                                                                                                                                                                                                 
  float c;                                                                                                                                                                                                                                 

  Line() {m = 0; c = 0;}                                                                                                                                                                                                                   
  Line(float mInput, float cInput) {m = mInput; c = cInput;}                                                                                                                                                                               
  float getM() const {return m;}                                                                                                                                                                                                           
  float getC() const {return c;}                                                                                                                                                                                                           
  void setM(float mInput) {m = mInput;}                                                                                                                                                                                                    
  void setC(float cInput) {c = cInput;}                                                                                                                                                                                                    

  bool operator==(const Line &anotherLine) const                                                                                                                                                                                           
    {                                                                                                                                                                                                                                      
      return (m == anotherLine.m);                                                                                                                                                                                                         
    }                                                                                                                                                                                                                                      
};                                                                                                                                                                                                                                         

namespace std                                                                                                                                                                                                                              
{                                                                                                                                                                                                                                          
  template <>                                                                                                                                                                                                                              
  struct hash<Line>                                                                                                                                                                                                                        
  {                                                                                                                                                                                                                                        
    size_t operator()(const Line& k) const                                                                                                                                                                                                 
      {                                                                                                                                                                                                                                    
        // Compute individual hash values for two data members and combine them using XOR and bit shifting                                                                                                                                 
        return ((hash<float>()(k.getM()) ^ (hash<float>()(k.getC()) << 1)) >> 1);                                                                                                                                                          
      }                                                                                                                                                                                                                                    
  };                                                                                                                                                                                                                                       
}                                                                                                                                                                                                                                          

int main()                                                                                                                                                                                                                                 
{                                                                                                                                                                                                                                          
  unordered_map<Line, int> t;                                                                                                                                                                                                              

  Line line1 = Line(3.0,4.0);                                                                                                                                                                                                              
  Line line2 = Line(3.0,5.0);                                                                                                                                                                                                              

  t.insert({line1, 1});                                                                                                                                                                                                                                                                                                                                                                                                                                      
  auto x = t.insert({line2, 2});                                                                                                                                                                                                           
  if (x.second == false)                                                                                                                                                                                                                   
    cout << "insert failed" << endl;                                                                                                                                                                                                       

  for(unordered_map<Line, int>::const_iterator it = t.begin(); it != t.end(); it++)                                                                                                                                                        
  {                                                                                                                                                                                                                                        
    Line t = it->first;                                                                                                                                                                                                                    
    cout << t.m << " " << t.c << "\n" ;                                                                                                                                                                                                    
  }                                                                                                                                                                                                                                        

  return 1;                                                                                                                                                                                                                                
}    

Your hash and operator == must satisfy a consistency requirement that they currently violate. 您的hashoperator ==必须满足它们当前违反的一致性要求。 When two objects are equal according to == , their hash codes must be equal according to hash . 当两个对象根据==相等时,它们的哈希码必须根据hash相等。 In other words, while non-equal objects may have the same hash code, equal objects must have the same hash code: 换句话说,虽然不相等的对象可能具有相同的哈希码,但是相等的对象必须具有相同的哈希码:

size_t operator()(const Line& k) const  {
    return hash<float>()(k.getM());
}   

Since you compare only one component for equality, and ignore the other component, you need to change your hash function to use the same component that you use to decide the equality. 由于您仅比较一个组件的相等性,而忽略另一个组件,因此需要更改哈希函数以使用用于确定相等性的相同组件。

you are using both the values of "m" and "c" in you hash, so 2 "Line" instances would have the same key if both their "m" and "c" are equal, which is not the case in your example. 您在哈希中同时使用了“ m”和“ c”的值,因此,如果两个“ Line”实例的“ m”和“ c”相等,则它们将具有相同的键,在您的示例中情况并非如此。 So if you do this : 因此,如果您这样做:

Line line1 = Line(3.0,4.0);                                                                                                                                                                                                              
Line line2 = Line(3.0,4.0);                                                                                                                                                                                                              

t.insert({line1, 1});                                                                                                                                                                                                                                                                                                                                                                                                                                      
auto x = t.insert({line2, 2});                                                                                                                                                                                                           
if (x.second == false)                                                                                                                                                                                                                   
  cout << "insert failed" << endl;  

you'll see that it will print "insert failed" 您会看到它将打印“插入失败”

You can always use custom function to compare the keys upon insertion: 您始终可以使用自定义功能在插入时比较键:

#include <iostream>
#include <unordered_map>

class Line {
private:
    float m;
    float c;
public:
    Line() { m = 0; c = 0; }
    Line(float mInput, float cInput) { m = mInput; c = cInput; }
    float getM() const { return m; }
    float getC() const { return c; }
};


struct hash
{
    size_t operator()(const Line& k) const 
    {
        return ((std::hash<float>()(k.getM()) ^ (std::hash<float>()(k.getC()) << 1)) >> 1);
    }
};

// custom key comparison
struct cmpKey
{
    bool operator() (Line const &l1, Line const &l2) const
    {
        return l1.getM() == l2.getM();
    }
};


int main()
{ 

    std::unordered_map<Line, int, hash, cmpKey> mymap; // with custom key comparisom

    Line line1 = Line(3.0, 4.0);
    Line line2 = Line(4.0, 5.0);
    Line line3 = Line(4.0, 4.0);

    auto x = mymap.insert({ line1, 1 });
    std::cout << std::boolalpha << "element inserted: " << x.second << std::endl;
    x = mymap.insert({ line2, 2 });
    std::cout << std::boolalpha << "element inserted: " << x.second << std::endl;
    x = mymap.insert({ line3, 3 });
    std::cout << std::boolalpha << "element inserted: " << x.second << std::endl;

    return 0;
}

Prints: 印刷品:

element inserted: true
element inserted: true
element inserted: false

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM