【问题标题】:How to use variants as the key in unordered_map?如何使用变体作为 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 看起来你不必做傻事。顺便说一句,杰瑞,不妨将这条评论正式化为答案。

标签: c++ unordered-map variant


【解决方案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?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-09
  • 2012-08-30
  • 2017-05-27
  • 2012-07-09
  • 2020-03-09
  • 2018-11-09
相关资源
最近更新 更多