如何使用变体作为 unordered_map 中的键?

Posted

技术标签:

【中文标题】如何使用变体作为 unordered_map 中的键?【英文标题】:How to use variants as the key in unordered_map? 【发布时间】:2020-08-26 23:27:53 【问题描述】:

如何在 unordered_map 中使用变体作为键?

例如,我想让下面的代码工作。

using VariantType = std::variant<int, std::string, unsigned int>;
std::unordered_map<VariantType, int, $some_hash_function$> m;

如何实现 $some_hash_function$?

【问题讨论】:

这是利息,不是渣。为什么以 Gobo Fraggle 的名义你想要多种类型的键?那一定是一个奇怪的用例。 “一个vector的变体”在哪里? 等等,变量en.cppreference.com/w/cpp/utility/variant/hash 已经有一个哈希值所以也许你根本不需要第三个参数。所有文档都说变体中的每个项目都必须有一个哈希函数——而你的就是这样。它在没有第三个参数的情况下编译,如果散列函数不存在,则不会编译。我只是用没有哈希的东西试了一下,但它没有用,所以我很肯定你不需要第三个参数。 闻起来像XY problem。 @JerryJeremiah 观点的演示:godbolt.org/z/db849x 看起来你不必做傻事。顺便说一句,杰瑞,不妨将这条评论正式化为答案。 【参考方案1】:

variant 已经有一个哈希模板特化:

http://en.cppreference.com/w/cpp/utility/variant/hash

唯一的条件是变体中的每个类型都必须有一个哈希函数:

如果std::hash&lt;std::remove_const_t&lt;Types&gt;&gt;... 中的每个特化都被启用,则特化std::hash&lt;std::variant&lt;Types...&gt;&gt; 被启用(参见std::hash),否则被禁用。

但是您的所有变体类型都有默认哈希,因此对于您的变体类型,它在没有第三个参数的情况下编译,因为标准哈希有效。但是,如果您的变体中有一个没有散列函数(或 == 运算符)的类型,那么它将无法编译并出现以下错误:

错误:静态断言失败:哈希函数必须可以使用键类型的参数调用

回到你的问题:

当变体类型具有哈希函数时:

#include <variant>
#include <unordered_map>
#include <string>
#include <iostream>
using VariantType = std::variant<int, std::string, unsigned int>;
std::unordered_map<VariantType, int> m =

 1, 1,
 2u, 2,
 std::string("string"),3
;
int main()

    VariantType v = std::string"string";
    std::cout << m[v];

你得到这个输出:

Program returned: 0
Program stdout
3

当不是所有的变体类型都有散列函数时:

#include <variant>
#include <unordered_map>
#include <string>
#include <iostream>
class UnhashedClass ;
using VariantType = std::variant<UnhashedClass, int, std::string>;
std::unordered_map<VariantType, int> m =

 1, 1,
 2u, 2,
 std::string("string"),3
;
int main()

    VariantType v = std::string"string";
    std::cout << m[v];

你得到这个输出:

Could not execute the program
Compiler returned: 1
Compiler stderr
...
error: static assertion failed: hash function must be invocable with an argument of key type
...

您可以在这里自己尝试:

https://godbolt.org/z/bnzcE9

【讨论】:

感谢您的回答。如果键是变体向量怎么办? @dalibocai 我会说我不会碰你手上的东西……咳咳。您甚至如何期望它具有一个独特的变体元组(在数学意义上,而不是在 C++ 意义上)与 map 中的值明确关联?如果你想将一个值与一组值关联起来,就像数据库一样,虽然是关系。 @Swift-FridayPie 我认为他正在尝试做的与 VBA 的集合方式相同 - 您可以通过索引或键检索集合项 - 所以您需要能够使用整数或字符串。 @dalibocai 我很高兴尝试找出一个变体向量,但我很好奇你会如何使用它?您传递给检索函数的变量值是否必须匹配向量中的任何项目?或者您会将向量传递给检索函数?如果您将单个变体传递给具有两个映射的检索函数是一个好主意:您在 ID 映射中查找您的变体,然后在具有值的映射中查找 ID。如果您将整个向量传递给检索函数,则:google.com/… @dalibocai 实际上,当我为“将单个变体传递给检索函数”版本建议两个映射时,也许您想要一个变体映射到 set::iterator 和一个包含该值的集合 - 也许根本不需要存储ID?

以上是关于如何使用变体作为 unordered_map 中的键?的主要内容,如果未能解决你的问题,请参考以下文章

我可以将一个类与模板中的另一个类关联(使用 C++17 变体)吗?

C++ 哈希表 - 如何解决 unordered_map 与自定义数据类型作为键的冲突?

unordered_map 迭代器指向 end(),如何从 unordered_map 中检索键?

将变体类型作为 REST 中的对象属性是不是合适?

使用自定义类类型作为键的 C++ unordered_map

使用自定义类类型作为键的 C++ unordered_map