у меня есть std::unordered_map
контейнер, где Key
может быть двух типов данных:
Но значение — это тип объекта, который одинаков для обоих типов ключей.
Я попробовал сделать одну вещь: std::variant
так что он может содержать оба типа. На основании некоторой проверки условия ключ устанавливается на один из типов:
void A::a() {
std::varaint<type1, type2> Id; //key
if (condition) {
Id = 64 bit unsigned value;
}
else {
Id = tuple<.....>;
}
}
unorderedmap[Id] = obj1;
// ^-- gives compile-time error
// (expecting Id specialized to either of the variant types)
Кроме того, аналогично этой функции есть несколько функций, где мы делаем find () на unordered_map
,
unorderedmap.find(Id);
// ^-- Here also, compiler is throwing similar error
Есть ли способ исправить вариант std ::, или я должен использовать другой подход?
Это, кажется, работает просто отлично:
#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
это расширенный вариант варианта, который вы хотите использовать — тесно эмулирующий динамические языки.
Если вы хотите поддержать std::tuple
У вас есть пара вариантов.
Просто используйте std::map
вместо std::unordered_map
, Вряд ли вы когда-нибудь сможете увидеть logN
и из опыта std::map
на самом деле будет быстрее (Вы также не будете убиты перефразировками, которые занимают столетие, что происходит каждый раз std::unordered_map
должен расти).
Продолжайте использовать 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{}
часть внутри заголовка, а затем просто включите этот заголовок, где вы хотите (я пропустил включение охранников, поэтому, как обычно, добавьте его). Если стандарт когда-либо догоняет и реализует хеширование кортежей, просто удалите заголовочный файл.
Других решений пока нет …