我有一个中等大小的代码库(>200 .cpp
),它使用函数hashCode()
返回哈希值:
class B01{ //a class
//..... complex thing ....
public: size_t hashCode(){ /* hash algorithm #H01 */}
};
class B02{ //just another unrelated class
//..... complex thing ....
public: size_t hashCode(){/* #H02 */} //This is the same name as above
};
字符串
我已经在不同的地方使用过它,例如在我的自定义数据结构中。它工作得很好。
现在,我想让std::
数据结构识别的哈希算法:
下面是我应该做的:-(修改自cppreference,我将此代码称为#D
)。
//#D
namespace std {
template<> struct hash<B01> {
std::size_t operator()(const B01& b) const {
/* hash algorithm #H01 */
}
};
}
型
如果我在每个类(B01
,B02
,.)中插入块#D
(使用适当的实现),我可以调用:
std::unordered_set<B01> b01s;
std::unordered_set<B02> b02s;
型
**不传递第二个模板参数,
我的哈希算法(#H01
)将被调用。(默认)
问题
为了让它识别我所有的B01::hashCode, B02::hashCode, ...
,
我必须将块#D
插入到所有200+ Bxx.h
中吗?
我可以只添加一个单个块#D
(在顶部标题中?)
并从那里,* 重新路由 * std::anyDataStructure
调用hashCode()
只要可能?
//pseudo code
namespace std{
template<> struct hash<X> {
std::size_t operator()(const X& x) const { // std::enable_if??
if(X has hashCode()){ //e.g. T=B01 or B02
make this template highest priority //how?
return hashCode();
}else{ //e.g. T=std::string
don't match this template;
}
}
};
}
型
这听起来像一个SFINAE问题给我。
旁注:SO中的The most similar question没有询问如何实现这一点。
Edit(Why don't I just refactor it?; 3 Feb 2017)
- 我不知道蛮力重构是不是一条正确的道路。我想可能有更好的方法。
hashCode()
是我的家我对它有感情- 我想让我的代码尽可能简短和干净。
std::
块是脏的。 - 可能只是我的好奇心,如果我固执地不重构我的代码,C++能走多远?
5条答案
按热度按时间tjjdgumg1#
不一定要这样,你也可以有一个仿函数:
字符串
别名为
std::unordered_set
,哈希类型为MyHash
:型
或者如果你还想提供 Equal 函子和分配器,可以更完整:
型
然后像使用
std::unordered_set
一样使用它(与任何Bxx一起使用):型
概念
如果你可以使用concepts,它们可以让你按照你想要的方式专门化
std::hash
类:型
r7xajy2e2#
您正在寻找的基于SFINAE的方法需要
std::hash
的部分专门化。如果您的类Bxx
是模板(如果它们是从CRTP基派生的话),则可以这样做。例如(注意在编辑中充实)字符串
produces(在MacOS上使用clang++)
B::hashvalue():return 42
this related answer也是我的一个类似问题。
然而,concepts是未来解决此类问题的方法。
ckx4rj1h3#
在创建条件以将std容器模板的hash参数默认为类组的成员方法时,应避免引入新问题。
经典的面向对象方法可能需要对200多个类进行模式化编辑,以确保它们提供std::hash容器使用的基础。下面给出了一些组转换的选项,以提供两个所需的方法。
两个模板
这两个模板将删除冗余并简化声明。
字符串
节省积分时间
一个超类的例子:
型
下面的sed表达式可以保存转换时间,假设代码使用{ inline。类似的表达式可以使用Boost或使用Python等脚本语言。
型
基于AST的工具会更可靠。This解释了如何使用clang功能进行代码转换。有新的补充,如C++代码转换的this Python controller。
讨论
对于哈希算法可以驻留的位置,有几个选项。
这里有一个编译单元,它提供了一个经典的演示,演示了如何实现所需的默认值和上面列出的其他三个目标,同时提供了为任何给定类定义哈希算法的灵活性。根据具体情况,可以删除各种功能。
型
brjng4g34#
我已经想出了一些看起来部分工作的东西。这是一个变通方案,允许你在实现
hashCode
的类型上使用std::hash
。看看:字符串
代码利用
hashable
的模板构造函数和模板操作符从任何实现hashCode
的类中检索哈希值。std::hash
专门化查找hashable
的示例,但模板化构造函数允许从具有hasCode
的类中构造示例。这里唯一的问题是,你将不得不编写
unordered_set
而不是std::unordered_set
来使用它,你将不得不确保std::unordered_set
没有以任何方式被带入范围。所以你不能在你的源代码中有像using namespace std
或using std::unordered_set
这样的东西。但是除了使用中的一些问题之外,这对你来说可能是可行的。当然,这只是解决真实的问题的一个权宜之计..
我还想指出的是,与此代码替代是一个错误.
编辑:
尝试跑步后:
型
我注意到有一些编译器错误。
我已经通过添加以下内容将
test
类更新为equality comparable
:型
到
test
,这使得:型
yc0p9oo05#
解决方案一
如果你可以用伪参数创建类
B01
,B02
,. class template,你可以简单地沿着std::hash
的特殊化,以获取伪模板参数:字符串
live demo(http://melpon.org/wandbox/permlink/M8tPQVQjmaZ6AcJF)
解决方案二(包含错误/不使用)
但我相信你想要的更像这样:
型
live demo(http://melpon.org/wandbox/permlink/SD7kOcXwMtLWF3te)的
(受此答案启发)
然而,只要这可以在gcc上工作,c++标准就不允许这样做(但我也不确定是否真的不允许这样做)。
编辑:
正如@巴里所指出的,这种gcc行为不是由c++标准强制执行的,因此绝对没有任何限制,它甚至可以在下一个gcc版本中工作.它甚至可以被认为是一个bug,因为它允许模板的部分专门化,而实际上并没有专门化该模板。
解决方案三(首选)
另一种方法是专门化
std::unordered_set
而不是std::hash
:型
根据here的讨论,它应该是完全有效的。它也适用于gcc,clang,icc,VS
为了能够使用代码而不干扰类的代码,我相信我们可以利用ADL规则来检查sfinae是否给定类不涉及std命名空间。你可以找到一个背景here。也归功于Cheers and hth. - Alf。方法可以改变如下:
型
gcc test(http://melpon.org/wandbox/permlink/D1dWjQn1V8ThrGRi)、clang test(http://melpon.org/wandbox/permlink/FHMZ9DWGY0RlhyDJ)、icc test(https://godbolt.org/g/CqcE1d) gcc 4.9(http://melpon.org/wandbox/permlink/3nAjQBRqVXQ7BlAX) VC(http://rextester.com/VTLZWI97536)