I'm trying to hold a polymorphic type as a key in a map.
I came up with the following two structures:
Note that Game
is an abstract class and the data structure I use is :
std::unordered_map<gamePtr,int> _allGames;
while gamePtr
is a typedef
for:
unique_ptr<Game>
template<>
struct std::hash<std::unique_ptr<Game>> {
size_t operator()(std::unique_ptr<Game> game) const {
return (std::hash<string>()(std::to_string(game->firstTeamFinalScore()) + game->firstTeam() + game->secondTeam()));
}
};
struct cmp_games {
bool operator() (std::unique_ptr<Game> game1, std::unique_ptr<Game> game2) const {
return *game1 == *game2;
}
};
The cmp_games
comparator seems to work fine but the std::hash
does not because it tries to copy a unique_ptr
(Which is ofc impossible) and I've no idea how to get over it.
Would love to hear some suggestions (If that is even possible).
EDIT: The comparator also doesn't seem to work properly. how do I make this map work correctly with unique_ptr
as a key?
EDIT2:
Came up with:
template<>
struct std::hash<std::unique_ptr<Game>> {
size_t operator()(const std::unique_ptr<Game>& game) const {
return (std::hash<string>()(std::to_string(game->firstTeamFinalScore()) + game->firstTeam() + game->secondTeam()));
}
};
template<>
struct std::equal_to<std::unique_ptr<Game>> {
bool operator() (const std::unique_ptr<Game>& game1,const std::unique_ptr<Game>& game2) const {
return *game1 == *game2;
}
};
Should they be enough?
The standard provides a specilization so that std::hash<unique_ptr<T>>
is the same as std::hash<T*>
. So provide a specialization for std::hash<Game *>
. For example:
#include <iostream>
#include <memory>
#include <unordered_map>
#include <cstdlib>
struct foo
{
foo(unsigned i) : i(i) {}
unsigned i;
};
namespace std {
template<>
struct hash<foo *>
{
size_t operator()(foo const *f) const
{
std::cout << "Hashing foo: " << f->i << '\n';
return f->i;;
}
};
}
int main()
{
std::unordered_map<std::unique_ptr<foo>, int> m;
m.insert(std::make_pair(std::unique_ptr<foo>(new foo(10)), 100));
m.insert(std::make_pair(std::unique_ptr<foo>(new foo(20)), 200));
}
Another option is to change your existing std::hash
specialization so that it takes the unique_ptr
by reference.
size_t operator()(std::unique_ptr<Game> const& game) const
// ^^^^^^ no more copying
EDIT: std::unique_ptr
provides comparison operators that compare the managed pointers. If you want the unordered_map
to test the Game
objects themselves for equality, provide an operator==
overload instead of specializing std::equal_to
inline bool operator==(const std::unique_ptr<Game>& game1,
const std::unique_ptr<Game>& game2)
{
return *game1 == *game2;
}
This, in turn, requires that you've provided an equality operator for Game
(or you could just add the logic to the function above).
inline bool operator==(Game const& game1, Game const& game2)
{
return // however you want to compare these
}