Кажется, это работает просто отлично:
#include <iostream>
#include <unordered_map>
#include <string>
#include <variant>
typedef std::variant<int, std::string> mytype;
std::unordered_map<mytype, int> m;
int main()
{
m[5] = 20;
std::cout << m[5];
m["hey"] = 10;
std::cout << m["hey"];
mytype tmp = "hey";
std::cout << m[tmp];
}
Таким образом, ответ в основном таков: убедитесь, что при попытке индексации карты с вариантом, индекс карты имеет тот же тип варианта.Если вы используете get
или , то это , вы даже можете заставить его работать, когда map
- это расширенный вариант варианта, который вы хотите использовать - тесно эмулируя динамические языки.
EDIT:
Если вы хотите поддержать std::tuple
, у вас есть несколько вариантов.
Опция 1
Просто используйте std::map
вместо std::unordered_map
.Маловероятно, что вы когда-либо сможете увидеть logN
, а из опыта std::map
будет на самом деле быстрее (вас также не убьет перефразировка, которая занимает столетие, что происходит каждый раз, когда std::unordered_map
должен расти).
Вариант 2
Продолжайте использовать std::unordered_map
, но используйте хеширование.Например, здесь со следующим адаптированным кодом:
#include <iostream>
#include <string>
#include <variant>
#include <unordered_map>
// #include "custom_tuple.h"
// CUSTOM_TUPLE.h
#include <tuple>
namespace std{
namespace
{
// Code from boost
// Reciprocal of the golden ratio helps spread entropy
// and handles duplicates.
// See Mike Seymour in magic-numbers-in-boosthash-combine:
// https://stackoverflow.com/questions/4948780
template <class T>
inline void hash_combine(std::size_t& seed, T const& v)
{
seed ^= hash<T>()(v) + 0x9e3779b9 + (seed<<6) + (seed>>2);
}
// Recursive template code derived from Matthieu M.
template <class Tuple, size_t Index = std::tuple_size<Tuple>::value - 1>
struct HashValueImpl
{
static void apply(size_t& seed, Tuple const& tuple)
{
HashValueImpl<Tuple, Index-1>::apply(seed, tuple);
hash_combine(seed, get<Index>(tuple));
}
};
template <class Tuple>
struct HashValueImpl<Tuple,0>
{
static void apply(size_t& seed, Tuple const& tuple)
{
hash_combine(seed, get<0>(tuple));
}
};
}
template <typename ... TT>
struct hash<std::tuple<TT...>>
{
size_t
operator()(std::tuple<TT...> const& tt) const
{
size_t seed = 0;
HashValueImpl<std::tuple<TT...> >::apply(seed, tt);
return seed;
}
};
}
// END CUSTOM_TUPLE.h
typedef std::variant<std::string, std::tuple<int, bool>> mytype;
std::unordered_map<mytype, int> m;
int main()
{
m[std::tuple{5, false}] = 20;
std::cout << m[std::tuple{5, false}];
m["hey"] = 10;
std::cout << m["hey"];
mytype tmp = "hey";
std::cout << m[tmp];
}
Вы можете поместить все внутри части namespace std{}
внутри заголовка, а затем просто включать этот заголовок, где бы вы ни находилисьхочу (я не включил охранников, так что добавьте, как обычно).Если стандарт когда-либо догоняет и реализует хэширование кортежей, просто удалите файл заголовка.