Mark
Mark

Reputation: 2321

c++11 polymorphic map keys static assertion failed: hash function must be invocable with an argument of key type

The error is

in file included from /usr/include/c++/8/unordered_map:46,
                from main.cpp:3:
/usr/include/c++/8/bits/hashtable.h: In instantiation of ‘class std::_Hashtable<MyClass*, std::pair<MyClass* const, double>, std::allocator<std::pair<MyClass* const, double> >, std::__detail::_Select1st, MyClassEquality, MyClassHash, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<true, false, true> >’:
/usr/include/c++/8/bits/unordered_map.h:105:18:   required from ‘class std::unordered_map<MyClass*, double, MyClassHash, MyClassEquality>’
main.cpp:35:68:   required from here
/usr/include/c++/8/bits/hashtable.h:195:21: error: static assertion failed: hash function must be invocable with an argument of key type
    static_assert(__is_invocable<const _H1&, const _Key&>{},
                    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/include/c++/8/bits/hashtable.h:197:21: error: static assertion failed: key equality predicate must be invocable with two arguments of key type
    static_assert(__is_invocable<const _Equal&, const _Key&, const _Key&>{},
                    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

the code is

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

using namespace std;

class MyClass {
public:
    long my_id;

    MyClass() {
        my_id = lrand48();
    }

    size_t id() const {
        hash<long> long_hash;
        return long_hash(my_id);
    }
};

struct MyClassHash {
public:
    size_t operator() (const MyClass * &c) const {
        return c->id();
    }
};

struct MyClassEquality {
public:
    bool operator() (const MyClass * &a, const MyClass * &b) const {
        return a->id() == b->id();
    }
};

class TestClass {
public:
    unordered_map<MyClass *, double, MyClassHash, MyClassEquality> my_map;;

/* makes no difference
 * TestClass() {
 *        my_map = unordered_map<MyClass *, double, MyClassHash, MyClassEquality>(42, MyClassHash(), MyClassEquality());
 *    }
 */

    void test() {
        for (auto p : my_map) {
            cout << p.first << "," << p.second << endl;
        }
    }
};

int main(int argc, char * argv[]) {
    auto * a = new TestClass();
    a->test();
    return 0;
}

The reason the map has pointers as keys is that the program needs to leverage polymorphism when it interacts with the contents of the map; is there a relatively simple oversight in the code or is there a more appropriate design pattern?

I've tried variations such as changing the structs to classes, not declaring the hash / equality operators public, etcetera; similarly the commented constructor.

Upvotes: 0

Views: 2387

Answers (1)

Mark
Mark

Reputation: 2321

In view of the fact that it looked as though there were limited specifically helpful results for the general design pattern of using polymorphic pointers as map keys, I'll just post the modified code as rafix07 rightly and very quickly suggested

struct MyClassHash {
public:
    size_t operator() (const MyClass * c) const {
        return c->id();
    }
};

struct MyClassEquality {
public:
    bool operator() (const MyClass * a, const MyClass * b) const {
        return a->id() == b->id();
    }
};

Upvotes: 1

Related Questions