Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ unordered_map fail when used with a vector as key

Tags:

Background: I am comming from the Java world and I am fairly new to C++ or Qt.

In order to play with unordered_map, I have written the following simple program:

#include <QtCore/QCoreApplication> #include <QtCore> #include <iostream> #include <stdio.h> #include <string> #include <unordered_map>  using std::string; using std::cout; using std::endl; typedef std::vector<float> floatVector;  int main(int argc, char *argv[]) {     QCoreApplication a(argc, argv);          floatVector c(10);     floatVector b(10);          for (int i = 0; i < 10; i++) {         c[i] = i + 1;         b[i] = i * 2;     }          std::unordered_map<floatVector, int> map;          map[b] = 135;     map[c] = 40;     map[c] = 32;        std::cout << "b -> " << map[b] << std::endl;     std::cout << "c -> " << map[c] << std::endl;     std::cout << "Contains? -> " << map.size() << std::endl;          return a.exec(); } 

Unfortunately, I am running into the folowing error which isn't inspiring. There is not even a line number.

:-1: error: collect2: ld returned 1 exit status

Any idea of the origin of the problem?

like image 660
Pierre-Antoine Avatar asked May 01 '12 22:05

Pierre-Antoine


People also ask

Can you use a vector as a key in unordered_map?

unordered_map uses vector as the key You can use the following method if you'd like to make the best of STL. }; Note that you can use any kind of operation to generate a hash. You just need to be creative so that collisions are minimized.

Can I use vector as a key in map c++?

In C++ we can use arrays or vector as a key against to a int value like: map<vector<int> ,int > m; Can I do same in MATLAB by containers.

Are vectors hashable?

Is vector hashable? Also as it is a mutable data structure it should not be allowed to be hashed or to be used as a key.

Can we hash a vector in c++?

Unlike a set of vectors, vectors are not arranged in any particular order in an unordered set of vectors. By default, C++ doesn't provide us the facility to create an unordered set of vectors. We are required to pass a hash function using which one can easily create an unordered set of vectors.


2 Answers

§23.2.5, paragraph 3, says:

Each unordered associative container is parameterized by Key, by a function object type Hash that meets the Hash requirements (17.6.3.4) and acts as a hash function for argument values of type Key, and by a binary predicate Pred that induces an equivalence relation on values of type Key.

Using vector<float> as Key and not providing explicit hash and equivalence predicate types means the default std::hash<vector<float>> and std::equal_to<vector<float>> will be used.

The std::equal_to for the equivalence relation is fine, because there is an operator == for vectors, and that's what std::equal_to uses.

There is however, no std::hash<vector<float>> specialization, and that's probably what the linker error you didn't show us says. You need to provide your own hasher for this to work.

An easy way of writing such an hasher is to use boost::hash_range:

template <typename Container> // we can make this generic for any container [1] struct container_hash {     std::size_t operator()(Container const& c) const {         return boost::hash_range(c.begin(), c.end());     } }; 

Then you can use:

std::unordered_map<floatVector, int, container_hash<floaVector>> map; 

Of course, if you need different equality semantics in the map you need to define the hash and equivalence relation appropriately.


1. However, avoid this for hashing unordered containers, as different orders will produce different hashes, and the order in unordered container is not guaranteed.

like image 141
R. Martinho Fernandes Avatar answered Sep 24 '22 20:09

R. Martinho Fernandes


I found R. Martinho Fernandes's answer unsuitable for competitive programming since most of the times you have to deal with a provided IDE and cannot use an external library such as boost. You can use the following method if you'd like to make the best of STL.

As already stated above, you just need to write a hash function. And it should specialize for the kind of data stored in your vector. The following hash function assumes int type data:

struct VectorHasher {     int operator()(const vector<int> &V) const {         int hash = V.size();         for(auto &i : V) {             hash ^= i + 0x9e3779b9 + (hash << 6) + (hash >> 2);         }         return hash;     } }; 

Note that you can use any kind of operation to generate a hash. You just need to be creative so that collisions are minimized. For example, hash^=V[i], hash|=V[i], hash+=V[i]*V[i] or even hash+=(V[i]<<i)*(V[i]<<i)*(V[i]<<i) are all valid until of course, your hash doesn't overflows.

Finally to use this hash function with your unordered_map, initialize it as follows:

unordered_map<vector<int>,string,VectorHasher> hashMap; 
like image 38
Chirag Arora Avatar answered Sep 26 '22 20:09

Chirag Arora