【问题标题】:How to make a map key to be of two different data types?如何使映射键具有两种不同的数据类型?
【发布时间】:2019-03-17 02:25:26
【问题描述】:

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

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

但是 value 是一个对象类型,这两种键类型都是一样的。

我尝试过的一件事是将密钥设为std::variant,以便它可以容纳两种类型。根据一些条件检查,key 被设置为以下类型之一:

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,或者我应该使用其他方法?

【问题讨论】:

  • 为什么不使用template
  • 您的问题是什么? “任何建议”在这里是题外话。工作代码在这里是题外话。如果您有特定问题,我们很乐意为您提供帮助,但您需要指定它并创建一个minimal reproducible example
  • 显示您的unordered_map 声明。你用的是哪个HashKeyEqual
  • 人们说“完整且可验证”的意思是,您不应该发布有拼写错误 (std::varaint) 或其他明显错误的代码(在 @987654335 范围之外使用 Id @)。如果您需要真正的帮助,您需要发布真正的代码。您应该能够生成一个小示例,任何人都可以尝试编译并获得您得到的 literal 确切错误,并编辑您的问题以提供该代码。

标签: c++ stdtuple std-variant


【解决方案1】:

这似乎工作得很好:

#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实际上会更快(你也不会被需要一个世纪的重新哈希谋杀,每次std::unordered_map必须增长时都会发生这种情况)。

选项 2

继续使用std::unordered_map,但实现散列。一个例子是here,修改后的代码如下:

#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{} 部分中的所有内容放在标头内,然后将该标头包含在您想要的任何位置(我省略了包含保护,因此请照常添加)。如果标准赶上并实现了元组散列,只需删除头文件。

【讨论】:

  • 当然有办法将其完全封装并获得安全保障。
  • @MilesBudnek 啊,我误会了。我更新了我的答案。
  • 实际问题是std::tuple 没有hash_map 特化,因此std::variant 也没有std::tuple 作为参数。所以将std::string替换为std::tuple&lt;int&gt;,代码将无法编译。
  • @bolov 如何对元组类型进行 hash_map 特化?有什么建议
  • @bvb 我已经更新了我的答案,看看它是否适合你。
猜你喜欢
  • 2022-11-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-27
  • 1970-01-01
  • 1970-01-01
  • 2021-07-09
  • 2014-02-18
相关资源
最近更新 更多