如何使映射键具有两种不同的数据类型?

时间:2018-10-11 22:14:14

标签: c++ stdtuple std-variant

我有一个std::unordered_map容器,其中Key可以是两种数据类型:

  • 64位无符号整数
  • 具有(8位无符号整数,8位无符号整数,16位无符号整数,32位无符号整数)的元组

但是值是两个键类型都相同的对象类型。

我尝试过的一件事是将键设置为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)

此外,类似于此函数,还有多个函数在unordered_map上执行find()。

unorderedmap.find(Id);
// ^-- Here also, compiler is throwing similar error

是否可以解决std :: variant,还是应该使用其他方法?

1 个答案:

答案 0 :(得分:3)

这似乎很好:

#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];
}

因此,答案基本上是:确保如果尝试使用变体对地图建立索引,则地图的索引具有相同的变体类型。如果您使用getthis,则当map是您要使用的变体的超集时,甚至可以使其工作-紧密模拟动态语言。

编辑:

如果您想支持std::tuple,则有两种选择。

选项1

只需使用std::map而不是std::unordered_map。您不太可能看到logN,而且根据经验std::map实际上会更快(您也不会因为每次进行{{{ 1}}必须增长)。

选项2

继续使用std::unordered_map,但实施哈希处理。一个示例为here,其代码如下:

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]; } 部分内部,然后仅在所需的位置包含该标题(我省略了包括警卫的内容,因此请照常添加)。如果该标准能够赶上并实现元组哈希,只需删除头文件即可。