Как использовать варианты в качестве ключа в unordered_map?

#c #unordered-map #variant

#c #неупорядоченная карта #вариант

Вопрос:

Как я могу использовать варианты в качестве ключа в unordered_map?

Например, я хотел бы заставить работать следующий код.

 using VariantType = std::variant<int, std::string, unsigned int>;
std::unordered_map<VariantType, int, $some_hash_function$> m;
  

Как мне реализовать $some_hash_function $?

Комментарии:

1. Это интерес, а не шлак. Почему, во имя Gobo Fraggle, вам нужен ключ нескольких типов? Это, должно быть, один из странных вариантов использования.

2. Где » vector множество вариантов»?

3. Подождите, хэш для variant уже есть en.cppreference.com/w/cpp/utility/variant/hash Так что, возможно, вам вообще не нужен третий параметр. Во всех документах говорится, что каждый элемент в variant должен иметь хэш-функцию — и у вас она есть. Он компилируется без третьего параметра, и if не компилировался бы, если бы хэш-функция не существовала. Я только что попробовал это с чем-то, у чего не было хэша, и это не сработало, поэтому я уверен, что вам не нужен третий параметр.

4. Похоже на проблему XY .

5. Демонстрация точки зрения @JerryJeremiah: godbolt.org/z/db849x Похоже, вам не нужно ничего делать. Кстати, Джерри, мог бы также оформить этот комментарий как ответ.

Ответ №1:

Для variant уже существует специализация шаблона хэша:

http://en.cppreference.com/w/cpp/utility/variant/hash

Единственное условие заключается в том, что каждый тип в variant должен иметь хэш-функцию:

Специализация std::hash<std::variant<Types...>> включена (см. std::hash), если включена каждая специализация в std::hash<std::remove_const_t<Types>>... , и отключена в противном случае.

Но все ваши типы вариантов имеют хэши по умолчанию, поэтому для ваших типов вариантов он компилируется без третьего параметра, потому что работает стандартный хэш. Однако, если бы в вашем варианте был тип, у которого не было хэш-функции (или оператора ==), то он не смог бы скомпилироваться с этой ошибкой:

ошибка: ошибка статического утверждения: хэш-функция должна быть вызываемой с аргументом типа key

Итак, вернемся к вашему вопросу:

Когда типы вариантов имеют хэш-функции:

 #include <variant>
#include <unordered_map>
#include <string>
#include <iostream>
using VariantType = std::variant<int, std::string, unsigned int>;
std::unordered_map<VariantType, int> m =
{
 {1, 1},
 {2u, 2},
 {std::string("string"),3}
};
int main()
{
    VariantType v = std::string{"string"};
    std::cout << m[v];
}
  

Вы получаете этот вывод:

 Program returned: 0
Program stdout
3
  

И когда не все типы вариантов имеют хэш-функции:

 #include <variant>
#include <unordered_map>
#include <string>
#include <iostream>
class UnhashedClass {};
using VariantType = std::variant<UnhashedClass, int, std::string>;
std::unordered_map<VariantType, int> m =
{
 {1, 1},
 {2u, 2},
 {std::string("string"),3}
};
int main()
{
    VariantType v = std::string{"string"};
    std::cout << m[v];
}
  

Вы получаете этот вывод:

 Could not execute the program
Compiler returned: 1
Compiler stderr
...
error: static assertion failed: hash function must be invocable with an argument of key type
...
  

Вы можете попробовать это сами здесь:

https://godbolt.org/z/bnzcE9

Комментарии:

1. Спасибо за ответ. Что, если ключ представляет собой вектор вариантов?

2. @dalibocai Я бы сказал, что я не собираюсь касаться того, что у вас есть… кхм. Как вы вообще ожидаете, что это сработает, если уникальный кортеж вариантов (в математическом смысле, не в смысле C ) будет однозначно связан со значением в map? Если вы хотите связать значение с набором значений, сделайте это так, как это делают базы данных, хотя и relationship.

3. @Swift-FridayPie Я думаю, что он пытается сделать то же самое, как VBA делает коллекции — вы можете получить элемент коллекции по индексу или по ключу — поэтому вам нужно иметь возможность использовать целое число или строку.

4. @dalibocai Я рад попытаться определить вектор вариантов, но мне любопытно, как бы вы это использовали? Должно ли значение варианта, которое вы передаете функции извлечения, соответствовать какому-либо элементу в векторе? Или вы бы передавали вектор в функцию извлечения? Если вы передаете один вариант в функцию поиска, хорошей идеей будет иметь две карты: вы ищете свой вариант в карте идентификаторов, а затем ищете идентификатор на карте, которая имеет значения. Если вы передаете весь вектор в функцию извлечения, то: google.com /…

5. @dalibocai На самом деле, когда я предложил две карты для версии «передача одного варианта в функцию извлечения», может быть, вы хотите, чтобы была установлена карта variant ::iterator и набор, содержащий значение — может быть, вообще нет необходимости хранить идентификатор?