Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can we define hashcode method within a class in C++

Tags:

c++

oop

hashcode

I am trying to implement a class in C++, and I want each class to have its own implementation of hashcode(basically to use it as a key in unordered_map & unordered_set)

for eg:

class CustomClass{
    int a;
    vector<int> b;
    string c;

    bool operator ==(const CustomClass& o) const{
        return ((a == o.a) && (b == o.b) && (c == o.c));
    }

    /* 
    Is it possible to define the hashcode function here instead of defining it outside the class. 
    size_t operator()() const {
        // Some custom logic for calculating hash of CustomClass using 
        // the hash Values of its individual fields

        std::size_t h = 0;
        for(int& t : b){
            h = (h ^ std::hash<int>()(t)) << 1;
        }
        return (h^(std::hash<int>()(a) << 1))^( std::hash<string>()(c) << 1);
    }
    */
};

Now, suppose I want to use this in an unordered_map like

int main(){
    unoredered_map<CustomClass, int> m;
}

I have two options,

i) Inject the hashcode in std namespace with Template specialization

namespace std {
  template <> struct hash<CustomClass> {
    size_t operator()(const CustomClass& o) const {
        // Some custom logic for calculating hash of CustomClass using 
        // the hash Values of its individual fields

        size_t h = 0;
        for(int& t : o.b){
            h = (h ^ std::hash<int>()(t)) << 1;
        }
        return (h^(std::hash<int>()(o.a) << 1))^( std::hash<string>()(o.c) << 1);
    }
  };
}

OR

ii.) Specify this function while creating the unordered_map (or unordered_set) every time while instantiating it, i.e

struct HashSpecialer {
  std::size_t operator()(const CustomClass& o) const {
      std::size_t h = 0;
      for(int& t : o.b){
         h = (h ^ std::hash<int>()(t)) << 1;
      }
      return (h^(std::hash<int>()(o.a) << 1))^( std::hash<string>()(o.c) << 1);
  }
};

and while instantiating the unordered_map, I have provide this struct.

int main(){
    unoredered_map<CustomClass, int, HashSpecialer> m;
}

I find both the methods, confusing to use (i) Pollutes the std namespace and (ii) makes it hard by remembering to provide the HashSpecializer every time I instantiate an unordered_map

Is it possible to provide the hashcode function within the class definition itself , as I described in the commented section in the code snippet above


Note : In java , we can override the hashCode() method in the class and we could achieve this functionality. Once I override the hashCode() method, I need not worry about it later.
public class CustomClass {
    int a;
    List<Integer> b;
    String c;

    // I Let my IDE generate these methods :D
    @Override public boolean equals(Object o)
    {
        if (this == o)
            return true;
        if (o == null || getClass() != o.getClass())
            return false;

        CustomClass that = (CustomClass) o;

        if (a != that.a)
            return false;
        if (b != null ? !b.equals(that.b) : that.b != null)
            return false;
        return c != null ? c.equals(that.c) : that.c == null;

    }

    // This one too :D
    @Override public int hashCode()
    {
        int result = a;
        result = 31 * result + (b != null ? b.hashCode() : 0);
        result = 31 * result + (c != null ? c.hashCode() : 0);
        return result;
    }
}

I am looking for something like this as this proves to be very handy.

like image 682
Anoop Avatar asked Feb 19 '16 18:02

Anoop


People also ask

What is hashCode of a class?

Hashcode is a unique code generated by the JVM at time of object creation. It can be used to perform some operation on hashing related algorithms like hashtable, hashmap etc. An object can also be searched with this unique code. Returns: It returns an integer value which represents hashCode value for this Method.

Is hashCode object class method?

Java equals() and hashCode() methods are present in Object class. So every java class gets the default implementation of equals() and hashCode().

Can we use equals method in interface?

If your interface contains other methods, Java will complain if you don't implement the interface fully by providing an implementation of those methods. But in the case of equals() , hashCode() and toString() (as well as a few others that you didn't mention) the implementation already exists.

Why equals () and hashCode () method used?

Both methods, equals() and hashcode() , are used in Hashtable , for example, to store values as key-value pairs. If we override one and not the other, there is a possibility that the Hashtable may not work as we want, if we use such object as a key.


3 Answers

I think the solution to your problem is to adjust your understanding of what is an aesthetically pleasing C++ program.

A specialization of std::hash doesn't pollute the std namespace, instead you should consider that std::hash is the customization point for controlling how unordered_map works with your class.

Such a specialization is part of the interface of the class (and can be a friend of the class) in exactly the same way as binary operators like operator +() should be a non-member functions, and still be part of the interface.

like image 104
Martin Bonner supports Monica Avatar answered Sep 24 '22 15:09

Martin Bonner supports Monica


I am not sure how do I handle the situation if CustomClass is defined in more than one namespace

That's what namespaces are for. Your problem solves itself :)

namespace std {
  template <> struct hash<NameSpace1::CustomClass> { ... };
  template <> struct hash<NameSpace2::CustomClass> { ... };
}

C++ is not Java. Forget any Java you know, it'll help you tremendously. Lots of Java idioms look very different in C++, or are simply useless or inapplicable in C++.

like image 24
Kuba hasn't forgotten Monica Avatar answered Sep 22 '22 15:09

Kuba hasn't forgotten Monica


C++'s design might be more verbose and cumbersome at a glance, but it's actually more flexible. All classes don't just have to know about hash codes; and different containers can require different hash constraints without intruding into the contained types at all.

That's what's so good about specialization. You can hook your code into the standard library without actually affecting it. After all, you're specializing for your own types, so it's not intrusive.

Specializing std::hash() for your type is thus perfectly okay, so long as you do intend to use that as a reasonable default. Otherwise, you may want to consider an alias template for the combination of std::unordered_map and your key type.

like image 20
Yam Marcovic Avatar answered Sep 24 '22 15:09

Yam Marcovic