Unordered_map из собственного типа путем перегрузки оператора() не работает

Что не так с operator() внутри Entity (хэшером)?

Копирование-вставка в отдельный struct работает:

#include <bitset>
#include <unordered_map>
using namespace std;

struct Entity {
  
  string name;
  
  size_t operator()(const Entity& k) const noexcept 
  { return std::hash<string>{}(k.name); }
  
  bool operator== (const Entity& e) const noexcept 
  { return e.name == name; }

};

struct KeyHasher {
  size_t operator()(const Entity& k) const noexcept
  { return std::hash<string>{}(k.name); }
};

int main(){
 // unordered_map<const Entity, bitset<24>, KeyHasher> m1; // OK
 unordered_map<const Entity, bitset<24>> m2; // unordered_map() ill-formed ?
  return 0;
}

Ошибка :

<source>: In function 'int main()':
<source>:25:43: error: use of deleted function 'std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::unordered_map() 
[with _Key = const Entity; _Tp = std::bitset<24>; _Hash = std::hash<const Entity>; _Pred = std::equal_to<const Entity>; _Alloc = std::allocator<std::pair<const Entity, std::bitset<24> > >]'
   25 |   unordered_map<const Entity, bitset<24>> m2;
      |                                           ^~
In file included from /opt/compiler-explorer/gcc-cxx-modules-trunk-20220427/include/c++/11.0.0/unordered_map:47,
                 from <source>:3:
/opt/compiler-explorer/gcc-cxx-modules-trunk-20220427/include/c++/11.0.0/bits/unordered_map.h:141:7: note: 'std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::unordered_map() [with _Key = const Entity; _Tp = std::bitset<24>; _Hash = std::hash<const Entity>; _Pred = std::equal_to<const Entity>; _Alloc = std::allocator<std::pair<const Entity, std::bitset<24> > >]'
 is implicitly deleted because the default definition would be ill-formed:
  141 |       unordered_map() = default;

беги

для чего вы ожидаете, что Entiry::operator() будет использоваться?

463035818_is_not_a_number 15.02.2023 11:16

код в вашей ссылке выглядит нормально, код компилируется. В чем проблема?

463035818_is_not_a_number 15.02.2023 11:16

карта никогда не использует value_type::operator(). непонятно, почему вы так думаете

463035818_is_not_a_number 15.02.2023 11:18

Нет смысла иметь константные ключи, поскольку существующие ключи остаются неизменными внутри карты.

molbdnilo 15.02.2023 11:21

@ 463035818 : вопрос обновлен. оператор вызывает std::hash(), чтобы предоставить уникальный хэш Entity.

ExpertNoob1 15.02.2023 11:22

@ExpertNoob1 Карта не использует перегрузку operator() для типа ключа для вычисления хеша.

molbdnilo 15.02.2023 11:25

@molbdnilo: значит, хэш-функция не может быть частью Entity, перегружая оператор, она должна быть собственной struct?

ExpertNoob1 15.02.2023 11:28

Довольно много, но это может быть любая вызываемая вещь или тип. (Ну, вы могли бы сказать unordered_map<Entity, bitset<24>, Entity>, но это привело бы к созданию Entity по умолчанию только для вычисления хэша других экземпляров Entity. Он не будет вызывать e(e), когда вы хотите добавить e.)

molbdnilo 15.02.2023 11:36
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
8
67
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Если вы не укажете хэш-функцию для своей карты, по умолчанию она будет равна std::hash<Key> с вашим типом ключа. Поэтому вам нужно будет определить эту специализацию std::hash

template<>
struct std::hash<const Entity>
{
    std::size_t operator()(const Entity const& k) const noexcept
    {
        return std::hash<string>{}(k.name);
    }
}

Спасибо. Мое замешательство возникло из-за использования техники перегрузки операторов с помощью unordered_set, которая действительно работает !! См. добавление Record в godbolt.org/z/zaPecMz13. Не улавливайте разницы между map и set в этом отношении. Оба нуждаются в хешировании ключей!

ExpertNoob1 15.02.2023 11:41

@ExpertNoob1 operator() должен находиться в отдельной структуре, которую затем можно указать как хэш. Не в классе, который вы там храните. Что касается unordered_set, вы указали bitset<24> в качестве хэша (который недействителен, но, похоже, ломается только при попытке туда что-то вставить).

HolyBlackCat 15.02.2023 11:49

@ExperNoob1 вы могли бы использовать unordered_map<const Entity, bitset<24>,Entity>, чтобы использовать Entity как тип ключа и как хэш одновременно, хотя это необычно и сбивает с толку. Я не знаю ни одного контейнера, использующего свои типы элементов operator().

463035818_is_not_a_number 15.02.2023 11:52

@ExpertNoob1 Попробуйте добавить что-нибудь в свой unordered_set и увидите, что это не удается. Затем сделайте это unordered_set<Record> и посмотрите, как он продолжает терпеть неудачу.

molbdnilo 15.02.2023 11:54

@HolyBlackCat первая версия ссылки OP использовала саму запись в качестве хэша, что могло бы работать, но этого не следует делать. Для текущей ссылки она не прерывается, потому что, пока вы не попытаетесь использовать функции, вызывающие хэшер, она на самом деле не попытается вызвать ее (и сломать). std::map нужно синтезировать hasher(pair<key, value>) из hasher(key), поэтому он всегда использует его.

Revolver_Ocelot 15.02.2023 11:55

@HolyBlackCat: я вдвойне сбит с толку!! Действительно, я использовал Record как собственный хэшер !! Никогда не было проблем с этой программой, но все же... Я согласен с @463035818: это смешно! О боже.

ExpertNoob1 15.02.2023 11:57

@ExpertNoob1 "Никогда не было проблем с этой программой" Какая программа? Использование типа в качестве собственного хэша обычно расточительно, так как каждый набор/карта будет хранить его дополнительный экземпляр только для вычисления хэшей. Не говоря уже о том, что пользователи хэша могут быть сбиты с толку () вычислением хэша.

HolyBlackCat 15.02.2023 12:11

Я сделал небольшой гаджет (многопоточное удаление огромного списка файлов/папок из входного файла), который использует unordered_set из Records для удаления дубликатов из списка папок, которые в результате становятся пустыми (тоже должны быть удалены), и он хешировал Record против самого себя. Собрал, работал годами без проблем. Но да: расточительно и запутанно (например, вы можете подумать, что это сработает с maps, и попытаетесь увековечить бессмыслицу!).

ExpertNoob1 15.02.2023 12:43

Другие вопросы по теме