Let's say we have a snippet of code as follows
#include <list>
#include <string>
#include <unordered_map>
int main() {
std::list<int> myList = {4, 1, 3, 2};
std::unordered_map<std::list<int>::iterator, std::string> myMap;
myMap[myList.begin()] = "first element";
}
This code in itself wouldn't work since we have to define a template specialization for std::hash
, like the one below
template<>
class std::hash<std::list<int>::iterator> {
public:
size_t operator()(std::list<int>::iterator const& it) const noexcept {
return hash<int*>()(&*it);
}
};
This works well.
But when I tried to generalize it and define the following instead,
template<typename T>
class std::hash<std::list<T>::iterator> {
public:
size_t operator()(std::list<T>::iterator const& it) const noexcept {
return hash<T*>()(&*it);
}
};
I see a static assertion failure again from the compiler. Why doesn't this work, and what is the right way to define std::hash
for any list-iterator?
-std=c++23
flag (C++23 standard)There's just no way to make this work:
template<typename T>
class std::hash<std::list<T>::iterator>
This is a non-deduced context, so deduction will always fail†. So you need to come up with some way to make your deduction viable.
The simplest approach is just to wrap:
template <typename T>
struct ListIterator {
std::list<T>::iterator;
};
template <typename T>
class std::hash<ListIterator<T>> { ... };
This is straightforwardly viable and works just fine, it's just that you need to have a unordered_map<ListIterator<T>, V>
instead of an unordered_map<std::list<T>::iterator, V>
. On the insertion side, shouldn't be a difference once you add an implicit constructor - but on the extraction side, yeah you might have to do an extra accessor here and there, but this'll get the job done.
†Although I feel like the language should be extended to cover the case of matching types whose name is actually C<T>::D
- there are going to be contexts that cannot be deducible at all (like add_const<T>::type
), but otherwise for the cases where you want stuff like this to work (and they do exist) you need to try to come up with wacky workarounds.