c++ - 为自定义对象指针重载 boost::hash_value
问题描述
介绍
大家好,我尝试使用boost::unordered_set
自定义类类型。该类存储有关坐标和其他几个值的信息,但只有坐标用于创建散列值。现在,如果我想插入一个点并且已经有一个坐标相等的点(因此是一个集合),我需要从原始对象更改第三个值(就像object.isDuplicate = true
非常简化的那样)。请不要过于拘泥于 bool 值和重复检测,因为它有点复杂,但它应该只表明我需要对存储的类进行非常量访问。我只能使用 boost 1.53 和 C++03 和 GCC 4.4.3
问题
现在的问题是,当我尝试插入一个点时,boost::unordered_set::insert
我得到一个点pair<iterator, bool>
,其中第一个成员是插入或原始条目的不可变迭代器,第二个是bool
指示值是否已插入。不幸的是,我不能用不可变的迭代器改变值,所以我不得不想一些不同的东西。所以我现在尝试在集合中存储一个指向我的对象的指针,然后通过这个指针访问它以更改值(这应该没问题,因为该值与散列值无关,因此不会改变键)。所以我试图重载boost::hash_value
函数以接受指向我的类的指针,如下所示:
size_t hash_value(const A * a) {
size_t seed = 0;
boost::hash_combine(seed, a->a);
boost::hash_combine(seed, a->b);
return seed;
}
但是unordered_set
即使我用unordered_set< A *, boost::hash<A *> >
. 对于散列方面:当我尝试使用没有指针的集合时,它工作正常,但我无法更改值。
可能的问题
我在boost::hash 参考中搜索了一下,发现这个重载 template<typename T> std::size_t hash_value(T* const&);
我认为它被用来代替我自己的重载(并且只是用对象地址散列)但是我想知道为什么我的编译器不提示重新定义这个函数(我在-Wall -Wextra -pedantic
启用标志的情况下编译。
问题
那么这是真正的问题吗?如果是这样,我怎么能告诉我的编译器明确地使用我的自定义哈希函数?
代码
最后我写了一个小例子来测试一切
#include <iostream>
#include <string>
#include <boost/functional/hash.hpp>
#include <boost/unordered_set.hpp>
using boost::unordered_set;
struct A {
double a;
double b;
bool isDup;
A(const double a, const double b): a(a), b(b), isDup(false) {}
A(const A & a): a(a.a), b(a.b), isDup(a.isDup) {}
/* Two equal As ought to have a bitwise equal floating point value so this is okay */
bool operator==(const A & a) const {
if (a.a != this->a) return false;
if (a.b != this->b) return false;
return true;
}
};
size_t hash_value(const A * a) {
size_t seed = 0;
boost::hash_combine(seed, a->a);
boost::hash_combine(seed, a->b);
std::cout << "Seed: " << seed << std::endl; /* This is not printed so i assume the function is not called */
return seed;
}
int main() {
A a1(1.2, 2.3);
A a2(2.3, 3.4);
A a3(3.4, 4.5);
A a4(a1);
unordered_set< A *, boost::hash<A *> > usa; /* This was unintended lol */
if ( ! usa.insert(&a1).second ) std::cout << "Error " << a1.a << ", " << a1.b << " is already in set" << std::endl;
if ( ! usa.insert(&a2).second ) std::cout << "Error " << a2.a << ", " << a2.b << " is already in set" << std::endl;
if ( ! usa.insert(&a3).second ) std::cout << "Error " << a3.a << ", " << a3.b << " is already in set" << std::endl;
if ( ! usa.insert(&a4).second ) {
/* This is not called */
std::cout << "Error " << a4.a << ", " << a4.b << " is already in set" << std::endl;
(*(usa.insert(&a4).first))->isDup = true;
}
}
解决方案
您的原始功能存在几个问题hash_value
:
- 它必须在
boost
命名空间内,因为boost::hash<T*>
调用boost::hash_value
会禁用依赖于参数的名称查找。 - 在模板名称查找执行两次:在声明和实例化时间。在实例化时,仅执行与参数相关的名称查找,但它被 1 禁用。这就是为什么必须在定义之前声明散列函数的原因
boost::hash
(在 include 之前boost/hash.hpp
)。
例如:
#include <cstddef> // std::size_t
struct A;
namespace boost { inline std::size_t hash_value(A* a); }
#include <iostream>
#include <string>
#include <boost/functional/hash.hpp>
#include <boost/unordered_set.hpp>
struct A { /*... */};
size_t boost::hash_value(A* a) {
size_t seed = 0;
boost::hash_combine(seed, a->a);
boost::hash_combine(seed, a->b);
std::cout << "Seed: " << seed << std::endl; /* This is not printed so i assume the function is not called */
return seed;
}
此外,您需要指定自己的元素比较类,boost::unordered_set
比较指针中的默认类。
附带说明一下,在组合多个成员的哈希方面,boost::hash
和的设计std::hash
并不理想。使用N3980 Types Don't Know #中的新哈希框架,我不能推荐足够多的东西。
推荐阅读
- java - Discord API (JDA) 从特定公会获取用户
- php - 如何从数组 PHP 中计算特定值?
- c# - 有没有办法重用具有不同结束参数的循环?
- sublimetext3 - Sublime Text 3 中视图的本地键绑定
- python - 用于创建要从 py-spark 中的业务规则执行的可执行文件的框架
- java - Android Java 应用程序启动时崩溃(无序静态字段索引 65535 和 0)
- r - 使用 R 中的前一个行字符串更新行字符串
- javascript - 带有香草js的scrollSpy,代码不起作用
- wcf - 是否可以在 WCF Webservice 中使用 Oauth?
- angular - 如何用角度的笑话模拟导入依赖项(ckeditor)