20

I want to use map in C++ STL to create assosiation between vector and int. But I got multiple compilation error with code presented below:

#include <vector>
#include <map>
#include <unordered_map>

using namespace std;

int main(void)
{
    unordered_map<vector<char>, int> mp;
    return 0;
}

And got this compilation error in VC++:

error C2280: 'std::hash<_Kty>::hash(const std::hash<_Kty> &)': attempting to reference a deleted function

However, if I change my code like presented below, then the code can be compiled properly:

#include <vector>
#include <map>
#include <unordered_map>

using namespace std;

int main(void)
{
    map<vector<char>, int> mp;
    return 0;
}

I have found this question asked in StackoverFlow, whose title is: C++ unordered_map using a custom class type as the key. But I wondered that why using map<> can pass the compilation check but unable with unordered_map<> ?

πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190
FS.L
  • 305
  • 1
  • 2
  • 9
  • `std::map` uses strict weak ordering and by-default`std::less` (which will implicitly by-default use some flavor of `operator <`). That is defined for vectors of comparable entities (such as `char`). `std::unordered_map` uses `std::hash`, and is *not* defined for your key type (`std::vector`). So either define it or provide your own hashing override type. – WhozCraig Jul 07 '18 at 05:42
  • Got this error value object do not have default constructor, has reference inside; end was a never ending loop ;-), now removing that reference. – Jan Feb 11 '21 at 20:57

2 Answers2

22

map requires that less-than comparison is implemented. Which it is, for a vector. But unordered_map requires a hash function, which you'll need to implement yourself. It's not a big deal, you can see how to use hash_combine to do that here: Fast hash function for `std::vector`

John Zwinck
  • 239,568
  • 38
  • 324
  • 436
6

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).

Paul Sanders
  • 24,133
  • 4
  • 26
  • 48
  • 1
    It is not really necessary to include every element in the hash function if the vectors are large. Hashing only the first ten elements might be reasonable. – John Zwinck Jul 07 '18 at 09:44
  • 1
    @JohnZwinck Yes, I guess that's true, since a few collisions more or less in the hash table probably don't matter much. You could get unlucky though, if many of the vectors in the unordered map happen to start with the same values. You'd get lots of collisions then. – Paul Sanders Jul 07 '18 at 15:43
  • This doesn't answer the question. – user2023370 Sep 27 '19 at 13:44
  • @user2023370 I am aware of that, but it still makes a contribution, see my introductory remarks. – Paul Sanders Sep 27 '19 at 15:15
  • Fair enough - it's not your fault the questioner has marked it correct. – user2023370 Sep 27 '19 at 17:36
  • @user2023370 FWIW, I think he did so because he agreed with me that choosing the right container was really at the heart of this question (there's a reason why `std::vector` doesn't have a `hash` function). But that's just a guess on my part, of course. – Paul Sanders Sep 27 '19 at 23:16