C++ Unordered_Map Using a Custom Class Type as the Key

using custom classes with unordered_map in cpp

Why would you want to convert the time to string first? Your goal should be a wide spreading of hash values with an inexpensive hash function, right? Also this is real time? In which case you get away with unsigned short for the members.

#include <unordered_map>
#include <functional>
#include <string>
#include <iostream>

class Time {
public:

Time(unsigned short h = 0, unsigned short m = 0, unsigned short s = 0) :
hours(h), minutes(m), seconds(s) {}

bool operator==(Time const& other) const {
return (seconds==other.seconds &&
minutes==other.minutes &&
hours==other.hours);
}

unsigned short hours, minutes, seconds;

};

std::ostream& operator<<(std::ostream& o, Time const& t) {
o << t.hours << ":" << t.minutes << ":" << t.seconds;
return o;
}

namespace std {
template<> struct hash<Time> {
size_t operator()(Time const& t) const {
return size_t(((t.seconds * 37 + t.minutes) * 37 + t.hours) * 37);
}
};
}

int main() {
std::unordered_map<Time, std::string> u;
u[Time(3,15,31)] = std::string("Hello world");
u[Time(3,15,32)] = std::string("foo");
u[Time(3,15,32)] = std::string("bar");
for (auto const& i : u) {
std::cout << i.first << " - " << i.second << std::endl;
}
return 0;
}

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

Your hash and operator == must satisfy a consistency requirement that they currently violate. When two objects are equal according to ==, their hash codes must be equal according to 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.

Unordered_map with custom class as key

emplace(d->get_name()->get_str(), d);

Here you try to construct a std::unordered_map<Symbol, Decl*>::value_type (i.e. a std::pair<Symbol, Decl*>) from a const std::string& and a Decl*.

The problem is that Symbol has no constructor taking a std::string, only a std::string const*.

This is the line in the error messages that gives the hint:

/Library/Developer/CommandLineTools/usr/include/c++/v1/utility:422:5: note: candidate constructor not viable: no known conversion from
'std::__1::basic_string<char>' to 'const const Symbol' for 1st argument
pair(_T1 const& __t1, _T2 const& __t2)

Custom structure as key in unordered_map

You need to const-qualify ItemHash::operator()(Item), i.e.,

struct ItemHash {
size_t operator()(Item item) const
// ^^^^ here
{
// as before...
}

Note that as @JeJo pointed out in the comments, you need the same fix for ItemEqual.

Custom class pointer as unordered map key

I tried your code and found 3 problems:

  1. Declaration of map: it should read std::unordered_map<MyClass*, int>
  2. call of undefined functions (tt1->find/tt1->end, should read tt1->testMap.XXX)
  3. Call of make_pair doesn't require template arguments. The compiler will infer them. This actually causes a problem, as the compiler tries to call make_pair(MyClass *&&, int &&). If I omit the template arguments, it works (make_pair(mc1, 10))

As for point 3:

make_pair is declared as follows in C++11 (C++14 just adds constexpr) (cppreference):

template< class T1, class T2 >
std::pair<V1,V2> make_pair( T1&& t, T2&& u );

For template argument deduction, the follwing rule applies (cf. cppreference)

4) If P is an rvalue reference to a cv-unqualified template parameter (so-called "forwarding reference"), and the corresponding function call argument is an lvalue, the type lvalue reference to A is used in place of A for deduction (Note: this is the basis for the action of std::forward)

(emphasis mine)

So the compiler will infer:

std::make_pair<MyClass *&, int>(MyClass *&, int &&);

where MyClass *& can bind to your actual argument.

If you directly specify the template types, the compiler will stick to

std::make_pair<MyClass *, int>(MyClass *&&, int &&).

As your argument is a lvalue, it cannot be converted to a rvalue-reference, and compilation fails



Related Topics



Leave a reply



Submit