C++14中使用mapbox::util::variant替代std::variant时std::hash编译报错的解决方法咨询
C++14中使用mapbox::util::variant替代std::variant时std::hash编译报错的解决方法咨询
嗨,这个问题我之前也碰到过,其实核心原因很明确:标准库的std::hash并没有为mapbox::util::variant提供现成的特化实现,当你直接用std::hash<mapbox::util::variant<...>>时,编译器会尝试生成默认的哈希结构体,但这个默认版本的构造函数是被删除的,于是就出现了你看到的报错。
下面给你两个实用的解决思路:
方法一:为mapbox::util::variant特化std::hash
我们可以在std命名空间里为mapbox::util::variant特化std::hash,借助mapbox::util::visit来处理variant中不同类型的哈希逻辑,同时结合variant的类型索引来减少不同类型值哈希冲突的概率。
代码示例如下:
#include <mapbox/variant.hpp> #include <functional> #include <type_traits> // 先实现一个哈希组合工具,减少不同类型值的哈希冲突 template <class T> void hash_combine(std::size_t& seed, const T& v) { std::hash<T> hasher; seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); } namespace std { template<typename... Ts> struct hash<mapbox::util::variant<Ts...>> { std::size_t operator()(const mapbox::util::variant<Ts...>& var) const noexcept { std::size_t seed = 0; mapbox::util::visit([&seed](const auto& val) { using ValueType = std::decay_t<decltype(val)>; // 先加入当前类型在variant中的索引哈希 size_t type_index = mapbox::util::variant<Ts...>::template index<ValueType>(); hash_combine(seed, type_index); // 再加入当前值的哈希 hash_combine(seed, val); }, var); return seed; } }; }
特化完成后,你原来的代码就可以正常编译运行了,不需要修改原来的调用逻辑。
方法二:自定义哈希函数对象(不修改std命名空间)
如果你不想特化std命名空间里的模板(毕竟C++标准对用户特化std模板有一定限制),可以自己写一个独立的哈希函数对象,逻辑和上面类似:
#include <mapbox/variant.hpp> #include <functional> #include <type_traits> template <class T> void hash_combine(std::size_t& seed, const T& v) { std::hash<T> hasher; seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); } struct VariantHash { template<typename... Ts> std::size_t operator()(const mapbox::util::variant<Ts...>& var) const noexcept { std::size_t seed = 0; mapbox::util::visit([&seed](const auto& val) { using ValueType = std::decay_t<decltype(val)>; size_t type_index = mapbox::util::variant<Ts...>::template index<ValueType>(); hash_combine(seed, type_index); hash_combine(seed, val); }, var); return seed; } };
这时候你需要把原来的调用代码改成:
return VariantHash{}(value.m_value);
两种方法都能解决问题,如果你希望代码风格和原来的std::hash调用保持一致,选方法一;如果你更在意不修改std命名空间的安全性,选方法二更稳妥。
备注:内容来源于stack exchange,提问作者Andreas
相关产品推荐
相关产品推荐

