Как сделать так, чтобы ключ карты был двух разных типов данных?

у меня есть std::unordered_map контейнер, где Key может быть двух типов данных:

  • 64-битное без знака int
  • кортеж, имеющий (8-битный без знака int, 8-битный без знака int, 16-битный unsigned-int, 32-битный unsigned int)

Но значение — это тип объекта, который одинаков для обоих типов ключей.

Я попробовал сделать одну вещь: 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 ::, или я должен использовать другой подход?

0

Решение

Это, кажется, работает просто отлично:

#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{} часть внутри заголовка, а затем просто включите этот заголовок, где вы хотите (я пропустил включение охранников, поэтому, как обычно, добавьте его). Если стандарт когда-либо догоняет и реализует хеширование кортежей, просто удалите заголовочный файл.

3

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]