Unordered_Map Constructor Error (Equal_To Templated Function)

unordered_map constructor error (equal_to templated function)

Both hashing_func and key_equal_fn should be functor objects (and not functions). In addition, their types must be provided to the unordered_map template, that is, the map should have this type:

unordered_map<string, string, hashing_func, key_equal_fn>

where hashing_func and key_equal_fn are functor classes:

struct hashing_func {
unsigned long operator()(const string& key) const {
unsigned long hash = 0;
for(size_t i=0; i<key.size(); i++)
hash += (71*hash + key[i]) % 5;
return hash;
}
};

struct key_equal_fn {
bool operator()(const string& t1, const string& t2) const {
return !(t1.compare(t2));
}
};

Then, mymap is defined in this way:

typedef unordered_map<string, string, hashing_func, key_equal_fn> MapType;
MapType::size_type n = 5;
MapType mymap(n, hashing_func(), key_equal_fn());

Alternatively, hashing_func and/or key_equal_fn can be functions but you have to wrapp them into std::function objects. That is,

unsigned long hashing_func(const string& key) {
unsigned long hash = 0;
for(size_t i=0; i<key.size(); i++)
hash += (71*hash + key[i]) % 5;
return hash;
}

bool key_equal_fn(const string& t1, const string& t2){
return !(t1.compare(t2));
}

and define mymap in this way

typedef unordered_map<string, string,
std::function<unsigned long(const string&)>,
std::function<bool(const string&, const string&)>> MapType;

MapType::size_type n = 5;
MapType mymap(n, hashing_func, key_equal_fn);

If you wish, you can use lambdas and refrain from writing the two funcions or functor classes:

typedef unordered_map<string, string,
std::function<unsigned long(const string&)>,
std::function<bool(const string&, const string&)>> MapType;

MapType mymap(n,
[](const string& key) -> unsigned long {
unsigned long hash = 0;
for(size_t i=0; i<key.size(); i++)
hash += (71*hash + key[i]) % 5;
return hash;
},
[](const string& t1, const string& t2) {
return !(t1.compare(t2));
});

Finally, my favorite is an all-lambdas solution

auto hashing_func = [](const string& key) -> unsigned long {
unsigned long hash = 0;
for(size_t i=0; i<key.size(); i++)
hash += (71*hash + key[i]) % 5;
return hash;
};

auto key_equal_fn = [](const string& t1, const string& t2) {
return !(t1.compare(t2));
};

typedef unordered_map<string, string,
decltype(hashing_func), decltype(key_equal_fn)> MapType;

MapType::size_type n = 5;
MapType mymap(n, hashing_func, key_equal_fn);

unordered_map with custom hashing/equal functions - functions don't get called

The problem is that you need to pass the types of your hash function and hash_key_equal function to your unordered_map, and then the actual functions to the ctor of the map.

Your unordered_map definition should look like this:

unordered_map<
std::string,
std::string,
std::function<unsigned long(std::string)>,
std::function<bool(std::string, std::string)>
> mymap(n, hashing_func, key_equal_fn<std::string>);

The unordered_map is a template and it looks like this:

template<
class Key,
class T,
class Hash = std::hash<Key>,
class KeyEqual = std::equal_to<Key>,
class Allocator = std::allocator<std::pair<const Key, T>>
> class unordered_map;

which means if you want to pass new Hash and KeyEqual functions you have to tell the template the types of these things.

Link isn't accessible anymore (Request Update):
Live Example

Compilation error related to map and unordered_map: attempting to reference a deleted function

Following on from @JohnZwinck's (excellent) answer, I would say that using std::unordered_map with a vector as a key is usually a bad idea, because of the likely high cost of implementing any kind of effective hashing function.

The link John gave expands on this, but, essentially, the hash function has to inspect every element in the vector every time anything needs to be hashed. If the vector is large, well, ouch!

So std::map is likely to be a better choice here, because std::less (-> operator<) is likely to be cheap - as soon as we hit an element of the vector whose value differs between the two operands, we are done. Worst case, it is no more expensive (although it's true that std::unordered_map is more efficient than std::map when a cheap and effective hashing function does exist, especially, for example, if the key is something like an int).

Errors occurs when accessing unordered_map by [] operator

The answers given are correct. I just want to add why the default constructor is not provided by the compiler in this case:

From online cpp reference

If no user-declared constructors of any kind are provided for a class type (struct, class, or union), the compiler will always declare a default constructor as an inline public member of its class.

But if user-declared constructors of any kind are provided, the compiler will not generate a default constructor.

If some user-declared constructors are present, the user may still force the automatic generation of a default constructor by the compiler that would be implicitly-declared otherwise with the keyword default. (since C++11)

So you can add the following line to the code in the public section of the class and your code will work as intended:

Person() = default;

Failure on initialization of unordered map

The issue is that std::map<key_type, value_type> requires a properly defined operator< for key_type, in this case your operator< is not const specified so it is incompatible with std::map as this data structure requires that the comparator not alter the key object in any way. Thus the solution is to mark class1::operator< as const.

The second error notes that no hash function-object has been applied for use with std::unordered_map, this would require the following framework:

auto class1_hasher = [](const class1& c) -> std::size_t { return {some hash based on c}; }
std::unordered_map<class1, std::string, decltype(class1_hasher)> um;

Why can't I compile an unordered_map with a pair as key?

You need to provide a suitable hash function for your key type. A simple example:

#include <unordered_map>
#include <functional>
#include <string>
#include <utility>

// Only for pairs of std::hash-able types for simplicity.
// You can of course template this struct to allow other hash functions
struct pair_hash {
template <class T1, class T2>
std::size_t operator () (const std::pair<T1,T2> &p) const {
auto h1 = std::hash<T1>{}(p.first);
auto h2 = std::hash<T2>{}(p.second);

// Mainly for demonstration purposes, i.e. works but is overly simple
// In the real world, use sth. like boost.hash_combine
return h1 ^ h2;
}
};

using Vote = std::pair<std::string, std::string>;
using Unordered_map = std::unordered_map<Vote, int, pair_hash>;

int main() {
Unordered_map um;
}

This will work, but not have the best hash-properties. You might want to have a look at something like boost.hash_combine for higher quality results when combining the hashes. This is also discussed in greater detail – including the aforementioned solution from boost – in this answer.

For real world use: Boost also provides the function set hash_value which already provides a hash function for std::pair, as well as std::tuple and most standard containers.


More precisely, it will produce too many collisions. E.g., every symmetric pair will hash to 0 and pairs that differ only by permutation will have the same hash. This is probably fine for your programming exercise, but might seriously hurt performance of real world code.



Related Topics



Leave a reply



Submit