使std的数据结构使用我现有的非静态散列函数“hashCode()”。默认 [英] Make std's data-structure use my existing non-static hash function "hashCode()" by default
问题描述
我有一个中等大小的代码库(> 200 .cpp
),它使用函数 hashCode()
来返回散列号: -
pre $ b $ {b class {
// ..... .....复杂的东西。 ...
public:size_t hashCode(){/ *散列算法#H01 * /}
};
class B02 {//只是另一个不相关的类
// .....复杂的东西....
public:size_t hashCode(){/ *#H02 * /} //这与
}的名字相同;
我在不同的地方使用过它,例如在我的自定义数据结构中。它很好。
现在,我想让 std ::
数据结构识别散列算法: -
这是我应该做的: - (从 cppreference ,我会称此代码 #D
)。
//#D
namespace std {
template<>结构散列< B01> {
std :: size_t operator()(const B01& b)const {
/ *散列算法#H01 * /
}
};
}
如果我插入块 #D $每个类(
B01
, B02
,...)中都有c $ c>调用: -
std :: unordered_set< B01> b01s;
std :: unordered_set< B02> b02s;
without 传递第二个模板参数,
和我的哈希算法(#H01
)将被调用。 (默认)
问题
让它识别我的所有 B01 :: hashCode,B02 :: hashCode,...
,
我必须插入块 #D
到所有200+ Bxx.h
?
我可以只需添加一个单个块 #D
(在顶部标题中?)
,然后从 re -route std :: anyDataStructure
在任何可能的情况下调用 hashCode()
//伪代码
名称空间std {
模板<>结构散列< X> {
std :: size_t operator()(const X& x)const {// std :: enable_if ??
if(X has hashCode()){//例如T = B01或B02
使此模板具有最高优先级//如何?
返回hashCode();
} else {//例如T = std :: string
不匹配此模板;
}
}
};
}
这听起来像是一个SFINAE问题。
附注: 在SO中最类似的问题并没有问及如何实现这一点。
编辑我是否重构了它?2017年2月3日)
- 我不知道蛮力重构是否正确路径。我想可能有更好的办法。
-
hashCode()
是我的家。我情绪上重视它。 - 我想尽可能保持我的代码简洁。
std ::
块很脏。 - 这可能只是我的好奇心。如果我顽固地不重构我的代码,C ++可以走多远? >解决方案1
如果您可以创建类 B01
, B02
,...具有虚拟参数的类模板,您可以简单地为具有虚拟模板参数的模板模板使用 std :: hash
的特化。 :
#include< iostream>
#include< unordered_set>
struct Dummy {};
模板< class = Dummy>
class B01 {
public:size_t hashCode()const {return 0; }
};
模板< class = Dummy>
class B02 {
public:size_t hashCode()const {return 0; }
};
namespace std {
template< template< class> class TT> struct hash< TT< Dummy>> {
std :: size_t operator()(const TT< Dummy>& x)const {
return x.hashCode();
}
};
}
int main(){
std :: unordered_set< B01<>>我们;
(void)us;
}
解决方案二(包含错误/不使用它) 但我相信你的愿望看起来更像这样: [live demo] 编辑: 正如@Barry所指出的,这个gcc行为是 not 由c ++标准强制执行,因此绝对没有担保,即使在下一个gcc版本中它也能工作......它甚至可以被视为一个bug,因为它允许模板的部分特化,事实上没有专门的模板。 解决方案三(优先) 特殊化 根据这里,它应该是完全有效的。它也可以在 gcc , clang , icc , VS 为了能够在不干扰的情况下使用代码类的代码我相信我们可以利用ADL规则做出sfinae检查,如果给定的类不涉及std命名空间。你可以在这里找到一个背景 。也感谢干杯和热情。 - Alf 。这种方法可以改变如下: [gcc test] , [clang test] , [icc test] [gcc 4.9] [VC] I have a moderate-size codebase (>200 I have used it in various locations, e.g. in my custom data-structure. It works well. Now, I want to make the hash algorithm recognized by Here is what I should do :- (modified from cppreference, I will call this code If I insert the block without passing the second template argument, To make it recognize all of my Can I just add a single block It sounds like a SFINAE question to me. Side note: The most similar question in SO didn't ask about how to achieve this. Edit (Why don't I just refactor it? ; 3 Feb 2017)
#include< iostream>
#include< unordered_set>
class B01 {
public:size_t hashCode()const {return 0; }
};
class B02 {
public:size_t hashCode()const {return 0; }
};
模板< class T,class>
使用enable_hash = T;
namespace std {
template< class T> struct hash {< enable_hash< T,decltype(std :: declval< T>()。hashCode())>> {
std :: size_t operator()(const T& x)const {
return x.hashCode();
}
};
}
int main(){
std :: unordered_set< B01>我们;
(void)us;
}
$ b (启发自这个答案)(但是我也不确定它是否实际上被禁止......)。在这种情况下请参阅此线程。
std :: unordered_set
而不是 std :: hash
:
#include< iostream>
#include< type_traits>
#include< unordered_set>
class specializeUnorderedSet {};
class B01:public specializeUnorderedSet {
public:size_t hashCode()const {return 0; }
};
class B02:public specializeUnorderedSet {
public:size_t hashCode()const {return 0; }
};
模板< class T>
struct my_hash {
std :: size_t operator()(const T& x)const {
return x.hashCode();
}
};
模板< class ...>
使用voider = void;
模板< class T,class = void>
struct hashCodeTrait:std :: false_type {};
模板< class T>
struct hashCodeTrait< T,voider< decltype(std :: declval< T>()。hashCode())>:std :: true_type {};
namespace std {
template< class T>
struct unordered_set< T,typename std :: enable_if< hashCodeTrait< T> :: value&& std :: is_base_of< specializeUnorderedSet,T> :: value,std :: hash< T> :: type,std :: equal_to< T>,std :: allocator< T>:
unordered_set< T,my_hash< T>,std :: equal_to< T>,std :: allocator< T> {};
int main(){
std :: unordered_set< B01>我们;
(void)us;
}
#include< utility>
#include< unordered_set>
#include< string>
#include< type_traits>
#include< functional>
模板<班级类型>
void ref(Type&&&& amp;& amp;班级类型>
constexpr auto involve_std()
- > bool
{
使用std :: is_same;
使用std :: declval;
返回不是is_same< void,decltype(ref(declval< Type&>()))> :: value;
}
class B01 {
public:size_t hashCode()const {return 0; }
};
class B02 {
public:size_t hashCode()const {return 0; }
};
模板< class T>
struct my_hash {
std :: size_t operator()(const T& x)const {
return x.hashCode();
}
};
模板< class ...>
struct voider {
using type = void;
};
模板< class T,class = void>
struct hashCodeTrait:std :: false_type {};
模板< class T>
struct hashCodeTrait< T,typename voider< decltype(std :: declval< T>()。hashCode())> :: type>:std :: true_type {};
namespace std {
template< class T>
struct unordered_set< T,typename std :: enable_if< hashCodeTrait< T> :: value&& < T>,std :: hash< T> :: type,std :: equal_to< T>,std :: allocator< T>:
unordered_set< T,my_hash< T> ;,std :: equal_to< T>,std :: allocator< T>> {};
int main(){
std :: unordered_set< B01> USB01;
std :: unordered_set< std :: string> USS;
static_assert(std :: is_base_of< std :: unordered_set< B01,my_hash< B01>>,std :: unordered_set< B01>> :: value,!);
static_assert(!std :: is_base_of< std :: unordered_set< std :: string,my_hash< std :: string>>,std :: unordered_set< std :: string>> :: value,! );
(void)usb01;
(void)uss;
}
.cpp
) that use a function hashCode()
to return hash number:-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::
data structure:-#D
).//#D
namespace std {
template<> struct hash<B01> {
std::size_t operator()(const B01& b) const {
/* hash algorithm #H01 */
}
};
}
#D
(with appropriate implementation) in every class (B01
,B02
,...), I can call :-std::unordered_set<B01> b01s;
std::unordered_set<B02> b02s;
and my hash algorithm (#H01
) will be called. (by default)Question
B01::hashCode, B02::hashCode, ...
,
do I have to insert the block #D
into all 200+ Bxx.h
?#D
(in a top header?)
and, from there, re-route std::anyDataStructure
to call hashCode()
whenever possible?//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;
}
}
};
}
hashCode()
is my home. I emotionally attach to it. std::
blocks are dirty.
Solution one
If you can make classes B01
, B02
, ... class templates with dummy parameters you could simply go along with the specialization of the std::hash
for template template that takes dummy template parameter:
#include <iostream>
#include <unordered_set>
struct Dummy {};
template <class = Dummy>
class B01{
public: size_t hashCode() const { return 0; }
};
template <class = Dummy>
class B02{
public: size_t hashCode() const { return 0; }
};
namespace std{
template<template <class> class TT> struct hash<TT<Dummy>> {
std::size_t operator()(const TT<Dummy>& x) const {
return x.hashCode();
}
};
}
int main() {
std::unordered_set<B01<>> us;
(void)us;
}
Solution two (contain error/don't use it)
But I believe what you desire looks more like this:
#include <iostream>
#include <unordered_set>
class B01{
public: size_t hashCode() const { return 0; }
};
class B02{
public: size_t hashCode() const { return 0; }
};
template <class T, class>
using enable_hash = T;
namespace std{
template<class T> struct hash<enable_hash<T, decltype(std::declval<T>().hashCode())>> {
std::size_t operator()(const T& x) const {
return x.hashCode();
}
};
}
int main() {
std::unordered_set<B01> us;
(void)us;
}
(Inspired by this answer)
However as long this can work on gcc it isn't really allowed by the c++ standard (but I'm also not sure if it is actually literally disallowed...). See this thread in this context.
Edit:
As pointed out by @Barry this gcc behaviour is not mandated by c++ standard and as such there is absolutely no guaranties it will work even in the next gcc version... It can be even perceived as a bug as it allows partial specialization of a template that in fact does not specialize that template.
Solution three (preffered)
Another way could be to specialize std::unordered_set
instead of std::hash
:
#include <iostream>
#include <type_traits>
#include <unordered_set>
class specializeUnorderedSet { };
class B01: public specializeUnorderedSet {
public: size_t hashCode() const { return 0; }
};
class B02: public specializeUnorderedSet {
public: size_t hashCode() const { return 0; }
};
template <class T>
struct my_hash {
std::size_t operator()(const T& x) const {
return x.hashCode();
}
};
template <class...>
using voider = void;
template <class T, class = void>
struct hashCodeTrait: std::false_type { };
template <class T>
struct hashCodeTrait<T, voider<decltype(std::declval<T>().hashCode())>>: std::true_type { };
namespace std{
template <class T>
struct unordered_set<T, typename std::enable_if<hashCodeTrait<T>::value && std::is_base_of<specializeUnorderedSet, T>::value, std::hash<T>>::type, std::equal_to<T>, std::allocator<T>>:
unordered_set<T, my_hash<T>, std::equal_to<T>, std::allocator<T>> { };
}
int main() {
std::unordered_set<B01> us;
(void)us;
}
According to the discussion presented here it should be perfectly valid. It also work in gcc, clang, icc, VS
To be able to use the code without interfering in the code of classes I believe we can utilize the ADL rules to make sfinae check if given class does not involve std namespace. You can find a background here. Credits also to Cheers and hth. - Alf. The approach could be change as follows:
#include <utility>
#include <unordered_set>
#include <string>
#include <type_traits>
#include <functional>
template< class Type >
void ref( Type&& ) {}
template< class Type >
constexpr auto involve_std()
-> bool
{
using std::is_same;
using std::declval;
return not is_same< void, decltype( ref( declval<Type &>() ) )>::value;
}
class B01 {
public: size_t hashCode() const { return 0; }
};
class B02 {
public: size_t hashCode() const { return 0; }
};
template <class T>
struct my_hash {
std::size_t operator()(const T& x) const {
return x.hashCode();
}
};
template <class...>
struct voider {
using type = void;
};
template <class T, class = void>
struct hashCodeTrait: std::false_type { };
template <class T>
struct hashCodeTrait<T, typename voider<decltype(std::declval<T>().hashCode())>::type>: std::true_type { };
namespace std{
template <class T>
struct unordered_set<T, typename std::enable_if<hashCodeTrait<T>::value && !involve_std<T>(), std::hash<T>>::type, std::equal_to<T>, std::allocator<T>>:
unordered_set<T, my_hash<T>, std::equal_to<T>, std::allocator<T>> { };
}
int main() {
std::unordered_set<B01> usb01;
std::unordered_set<std::string> uss;
static_assert(std::is_base_of<std::unordered_set<B01, my_hash<B01>>, std::unordered_set<B01>>::value, "!");
static_assert(!std::is_base_of<std::unordered_set<std::string, my_hash<std::string>>, std::unordered_set<std::string>>::value, "!");
(void)usb01;
(void)uss;
}
[gcc test], [clang test], [icc test] [gcc 4.9] [VC]
这篇关于使std的数据结构使用我现有的非静态散列函数“hashCode()”。默认的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!