#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
...
Вы можете попробовать это сами здесь:
Комментарии:
1. Спасибо за ответ. Что, если ключ представляет собой вектор вариантов?
2. @dalibocai Я бы сказал, что я не собираюсь касаться того, что у вас есть… кхм. Как вы вообще ожидаете, что это сработает, если уникальный кортеж вариантов (в математическом смысле, не в смысле C ) будет однозначно связан со значением в map? Если вы хотите связать значение с набором значений, сделайте это так, как это делают базы данных, хотя и relationship.
3. @Swift-FridayPie Я думаю, что он пытается сделать то же самое, как VBA делает коллекции — вы можете получить элемент коллекции по индексу или по ключу — поэтому вам нужно иметь возможность использовать целое число или строку.
4. @dalibocai Я рад попытаться определить вектор вариантов, но мне любопытно, как бы вы это использовали? Должно ли значение варианта, которое вы передаете функции извлечения, соответствовать какому-либо элементу в векторе? Или вы бы передавали вектор в функцию извлечения? Если вы передаете один вариант в функцию поиска, хорошей идеей будет иметь две карты: вы ищете свой вариант в карте идентификаторов, а затем ищете идентификатор на карте, которая имеет значения. Если вы передаете весь вектор в функцию извлечения, то: google.com /…
5. @dalibocai На самом деле, когда я предложил две карты для версии «передача одного варианта в функцию извлечения», может быть, вы хотите, чтобы была установлена карта variant ::iterator и набор, содержащий значение — может быть, вообще нет необходимости хранить идентификатор?