В следующем коде я определил аргументы шаблона Hash и KeyEqual для unordered_map. Я ожидаю, что результат будет 1 1 1 1, но на самом деле это 1 1 0 1. Почему это происходит? Это потому, что std::equal_to<Base*> не используется при сравнении карт с ==?
#include <iostream>
#include <unordered_map>
using std::cout;
using std::endl;
class Base {
public:
int x;
Base(int _x) : x(_x) {}
bool operator==(const Base& another) const {
return x == another.x;
}
size_t hash() const {return x;}
};
template <>
struct std::hash<Base> {
size_t operator()(const Base& r) const {
return r.hash();
}
};
template <>
struct std::hash<Base*> {
size_t operator()(const Base *r) const {
return r->hash();
}
};
template <>
struct std::equal_to<Base*> {
bool operator()(const Base *r1, const Base *r2) const {
return (*r1) == (*r2);
}
};
int main(int, char**){
Base b1(1);
Base b2(2);
Base bb1(1);
Base bb2(2);
cout << (b1 == bb1) << endl;
std::unordered_map<Base, int> map1;
map1.emplace(b1, 1);
map1.emplace(b2, 2);
std::unordered_map<Base, int> map2;
map2.emplace(bb1, 1);
map2.emplace(bb2, 2);
cout << (map1 == map2) << endl;
std::unordered_map<Base*, int, std::hash<Base*>, std::equal_to<Base*>> map11;
map11.emplace(&b1, 1);
map11.emplace(&b2, 2);
std::unordered_map<Base*, int, std::hash<Base*>, std::equal_to<Base*>> map22;
map22.emplace(&bb1, 1);
map22.emplace(&bb2, 2);
cout << (map11 == map22) << endl;
std::unordered_map<Base*, int, std::hash<Base*>, std::equal_to<Base*>> map33;
map33.emplace(&b1, 1);
map33.emplace(&b2, 2);
cout << (map11 == map33) << endl;
}





operator== обходит их KeyEqual ради std::unordered_mapВопреки интуиции, оператор == для std::unordered_map не заботится ни о std::hash, ни о std::key_equal; он использует встроенный оператор == для вашего типа.
Два неупорядоченных контейнера a и b сравниваются равными, если
a.size() == b.size()и для каждой группы эквивалентных ключей[Ea1, Ea2), полученной изa.equal_range(Ea1), существует группа эквивалентных ключей[Eb1, Eb2), полученная изb.equal_range(Ea1), такая, чтоis_permutation(Ea1, Ea2, Eb1, Eb2)возвращаетtrue.
Обратите внимание, что диапазоны, которые проверяются на равенство (в вашем случае они будут просто содержать по одному указателю каждый), не используют KeyEqual предоставленные контейнеру. Он определяется с помощью is_permutation без KeyEqual, который просто использует встроенный оператор ==.
Контейнеры, как правило, не учитывают KeyEqual, Less (в случае std::set) и т. д., которые вы им предоставляете. Все операторы сравнения просто перенаправляются к содержащимся в них элементам, и такая конструкция единообразна, хотя и противоречива здравому смыслу.
Для двух std::unordered_map с двумя (с состоянием) KeyEqual есть другая мотивация:
В общем, вычисление перестановок — это квадратичная операция. Однако, учитывая два неупорядоченных контейнеры, которые используют одни и те же функции хеширования и эквивалентности ключей, элементы будут разделены на группы эквивалентности ключей, которые делают сравнение намного более эффективным.
- N2986: Сравнение равенства для неупорядоченных контейнеров
См. также Невозможно сравнить std::unorded_set с пользовательским KeyEqual
Похоже, что в этом примере функция сравнения на равенство ЯВЛЯЕТСЯ уточнением раздела, созданного KeyEqual. То есть a == b подразумевает KeyEqual(a, b) для всех возможных ключей.
@ChrisDodd, ты прав, так что это четко определено. Это просто четко определено, чтобы получить неожиданный результат.
Я настроил сравнение, сравнив размеры карт и пары ключ-значение, и это работает нормально.