当前位置:  开发笔记 > 编程语言 > 正文

使std的数据结构默认使用我现有的非静态哈希函数"hashCode()"

如何解决《使std的数据结构默认使用我现有的非静态哈希函数"hashCode()"》经验,为你挑选了3个好方法。

我有一个中等大小的代码库(> 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 {
        std::size_t operator()(const B01& b) const {
            /* hash algorithm #H01 */
        }
    };
}

如果我插入块#D中的每一个类((与适当的执行)B01,B02...),我可以打电话: -

std::unordered_set b01s;
std::unordered_set b02s;

传递第二个模板参数,
我的哈希算法(#H01)将被调用.(默认)

为了让它能识别我的所有内容B01::hashCode, B02::hashCode, ...,
我是否必须将该块#D插入所有200+ Bxx.h

我可以再补充一个单一的#D(在顶部集流管?)
,并从那里,重新路由 std::anyDataStructure调用hashCode()尽可能?

//pseudo code
namespace std{
    template<> struct hash   {
        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中最相似的问题没有询问如何实现这一目标.

编辑(为什么我不重构它?; 2017年2月3日)

我不知道蛮力重构是否是正确的道路.我猜可能有更好的方法.

hashCode()是我的家.我情绪上依附于它.

我想尽可能保持我的代码简洁. std::块很脏.

这可能只是我的好奇心.如果我顽固不重构我的代码,C++可以走多远?

O'Neil.. 9

它不一定是这样,你也可以有一个仿函数:

struct MyHash {
    template 
    auto hashCode(const T & t, int) const -> decltype(t.hashCode()) {
        return t.hashCode();
    }
    template 
    auto hashCode(const T & t, long) const -> decltype(std::hash{}(t)) {
        return std::hash{}(t);
    }

    template 
    auto operator()(const T & t) const -> decltype(hashCode(t,42)) {
        return hashCode(t,42);
    }
};

并有一个别名std::unordered_setMyHash哈希类型:

template 
using my_unordered_set = std::unordered_set;

或者更完整,如果您还希望能够提供Equal仿函数和分配器:

template<
    class Key,
    class KeyEqual = std::equal_to,
    class Allocator = std::allocator
>
using my_unordered_set = std::unordered_set;

然后使用它(使用你的任何Bxx)就像你使用的那样std::unordered_set:

int main() {
    my_unordered_set b01s;
    my_unordered_set b02s;

    // or lonely with your type:
    B01 b01{/*...*/};
    std::cout << MyHash{}(b01) << std::endl;

    // or any other:
    std::string str{"Hello World!"};
    std::cout << MyHash{}(str) << std::endl;
}

概念

如果您可以使用概念,它们可以让您std::hash按照自己的方式专门化课程:

template 
concept bool HashCodeConcept = requires(T const & t)
{
    {t.hashCode()} -> std::size_t;
};

namespace std {
    template  requires HashCodeConcept  
    struct hash {
        std::size_t operator()(const T& t) const {
            return  t.hashCode();
        }
    };
}


Douglas Dase.. 8

在创建条件以将std容器模板的hash参数默认为类组的成员方法时,应避免引入新问题.

冗余

便携性问题

奥术结构

经典的面向对象方法可能需要对200多个类进行图案化编辑,以确保它们提供std :: hash容器使用的基础知识.下面给出了一些用于组转换的选项,以提供两种所需的方法.

公共hashCode()在具体类中定义,它对于该类是唯一的,或者如果它遵循跨类通用的模式,则通过继承.

定义了公共运算符==().

两个模板

这两个模板将删除冗余并简化声明,如图所示.

template 
    struct HashStruct {
        std::size_t operator()(const T & t) const {
            return t.hashCode();
        } };
template 
    using SetOfB = std::unordered_set>;

节省集成时间

一个超级类的例子:

class AbstractB {
    ...
    virtual std::size_t hashCode() const {
        return std::hash{}(ms1)
                ^ std::hash{}(ms2);
    } }

假设代码使用{inline,则以下sed表达式可以节省转换时间.类似的表达式可以与Boost一起使用或使用像Python这样的脚本语言.

"s/^([ \t]*class +B[a-zA-Z0-9]+ *)(:?)(.*)$"
        + "/\1 \2 : public AbstractB, \3 [{]/"
        + "; s/ {2,}/ /g"
        + "; s/: ?:/:/g"

基于AST的工具会更可靠. 这解释了如何使用clang功能进行代码转换.有一些新增功能,例如C++代码转换的Python控制器.

讨论

哈希算法可以驻留在几个选项中.

std容器声明的抽象类的方法

具体类的方法(例如示例中的#H01)

结构模板(通常适得其反并且不透明)

默认的std :: hash

这是一个编译单元,它提供了一个清晰的演示,说明如何实现所需的默认值以及上面列出的其他三个目标,同时为任何给定类定义散列算法提供灵活性.根据具体情况,可以删除各种功能.

#include 
#include 
#include 

template 
    struct HashStructForPtrs {
        std::size_t operator()(const T tp) const {
            return tp->hashCode(); } };
template 
    using SetOfBPtrs = std::unordered_set>;

template 
    struct HashStruct {
        std::size_t operator()(const T & t) const {
            return t.hashCode(); } };
template 
    using SetOfB = std::unordered_set>;

class AbstractB {
    protected:
        std::string ms;
    public:
        virtual std::size_t hashCode() const {
            return std::hash{}(ms); }
        // other option: virtual std::size_t hashCode() const = 0;
        bool operator==(const AbstractB & b) const {
            return ms == b.ms; } };

class B01 : public AbstractB {
    public:
        std::size_t hashCode() const {
            return std::hash{}(ms) ^ 1; } };

class B02 : public AbstractB {
    public:
        std::size_t hashCode() const {
            return std::hash{}(ms) ^ 2; } };

int main(int iArgs, char * args[]) {

    SetOfBPtrs setOfBPointers;
    setOfBPointers.insert(new B01());
    setOfBPointers.insert(new B02());

    SetOfB setOfB01;
    setOfB01.insert(B01());

    SetOfB setOfB02;
    setOfB02.insert(B02());

    return 0; };

我必须为我目前拥有的每个'Bxx`添加`template <> struct hash `,所以它仍然非常繁琐.但是,嘿,谢谢你的尝试和分享! (2认同)

谢谢.我以为我只是愚蠢无法找到一种方法来优雅地解决这个问题.知道某些语言没有这个问题也很好.我可以以某种方式安息吧.:) (2认同)


Walter.. 6

您正在寻找的基于SFINAE的方法需要部分专业化std::hash.如果您的类Bxx是模板(如果它们是从CRTP基础派生的话),则可以执行此操作.例如(注意在编辑中充实)

#include 
#include 
#include 

template
struct B {
  B(int i) : x(i) {}
  std::size_t hashCode() const
  {
    std::cout<<"B::hashCode(): return "<().hashCode())> 
using enable_if_has_hashCode = T;

namespace std {
  template class T, typename... As> 
  struct hash>> 
  {
    std::size_t operator()(const T& x) const
    { return x.hashCode(); }
  };
  // the following would not work, as its not a partial specialisation
  //    (some compilers allow it, but clang correctly rejects it)
  // tempate
  // struct hash>
  // { /* ... */ }; 
}

int main()
{
  using B00 = B;
  B00 b(42);
  std::unordered_set set;
  set.insert(b);
}

生成(在MacOS上使用clang ++)

B :: hashvalue():返回42

另见我的类似问题的相关答案.

然而,概念是解决这类问题的未来之路.



1> O'Neil..:

它不一定是这样,你也可以有一个仿函数:

struct MyHash {
    template 
    auto hashCode(const T & t, int) const -> decltype(t.hashCode()) {
        return t.hashCode();
    }
    template 
    auto hashCode(const T & t, long) const -> decltype(std::hash{}(t)) {
        return std::hash{}(t);
    }

    template 
    auto operator()(const T & t) const -> decltype(hashCode(t,42)) {
        return hashCode(t,42);
    }
};

并有一个别名std::unordered_setMyHash哈希类型:

template 
using my_unordered_set = std::unordered_set;

或者更完整,如果您还希望能够提供Equal仿函数和分配器:

template<
    class Key,
    class KeyEqual = std::equal_to,
    class Allocator = std::allocator
>
using my_unordered_set = std::unordered_set;

然后使用它(使用你的任何Bxx)就像你使用的那样std::unordered_set:

int main() {
    my_unordered_set b01s;
    my_unordered_set b02s;

    // or lonely with your type:
    B01 b01{/*...*/};
    std::cout << MyHash{}(b01) << std::endl;

    // or any other:
    std::string str{"Hello World!"};
    std::cout << MyHash{}(str) << std::endl;
}

概念

如果您可以使用概念,它们可以让您std::hash按照自己的方式专门化课程:

template 
concept bool HashCodeConcept = requires(T const & t)
{
    {t.hashCode()} -> std::size_t;
};

namespace std {
    template  requires HashCodeConcept  
    struct hash {
        std::size_t operator()(const T& t) const {
            return  t.hashCode();
        }
    };
}



2> Douglas Dase..:

在创建条件以将std容器模板的hash参数默认为类组的成员方法时,应避免引入新问题.

冗余

便携性问题

奥术结构

经典的面向对象方法可能需要对200多个类进行图案化编辑,以确保它们提供std :: hash容器使用的基础知识.下面给出了一些用于组转换的选项,以提供两种所需的方法.

公共hashCode()在具体类中定义,它对于该类是唯一的,或者如果它遵循跨类通用的模式,则通过继承.

定义了公共运算符==().

两个模板

这两个模板将删除冗余并简化声明,如图所示.

template 
    struct HashStruct {
        std::size_t operator()(const T & t) const {
            return t.hashCode();
        } };
template 
    using SetOfB = std::unordered_set>;

节省集成时间

一个超级类的例子:

class AbstractB {
    ...
    virtual std::size_t hashCode() const {
        return std::hash{}(ms1)
                ^ std::hash{}(ms2);
    } }

假设代码使用{inline,则以下sed表达式可以节省转换时间.类似的表达式可以与Boost一起使用或使用像Python这样的脚本语言.

"s/^([ \t]*class +B[a-zA-Z0-9]+ *)(:?)(.*)$"
        + "/\1 \2 : public AbstractB, \3 [{]/"
        + "; s/ {2,}/ /g"
        + "; s/: ?:/:/g"

基于AST的工具会更可靠. 这解释了如何使用clang功能进行代码转换.有一些新增功能,例如C++代码转换的Python控制器.

讨论

哈希算法可以驻留在几个选项中.

std容器声明的抽象类的方法

具体类的方法(例如示例中的#H01)

结构模板(通常适得其反并且不透明)

默认的std :: hash

这是一个编译单元,它提供了一个清晰的演示,说明如何实现所需的默认值以及上面列出的其他三个目标,同时为任何给定类定义散列算法提供灵活性.根据具体情况,可以删除各种功能.

#include 
#include 
#include 

template 
    struct HashStructForPtrs {
        std::size_t operator()(const T tp) const {
            return tp->hashCode(); } };
template 
    using SetOfBPtrs = std::unordered_set>;

template 
    struct HashStruct {
        std::size_t operator()(const T & t) const {
            return t.hashCode(); } };
template 
    using SetOfB = std::unordered_set>;

class AbstractB {
    protected:
        std::string ms;
    public:
        virtual std::size_t hashCode() const {
            return std::hash{}(ms); }
        // other option: virtual std::size_t hashCode() const = 0;
        bool operator==(const AbstractB & b) const {
            return ms == b.ms; } };

class B01 : public AbstractB {
    public:
        std::size_t hashCode() const {
            return std::hash{}(ms) ^ 1; } };

class B02 : public AbstractB {
    public:
        std::size_t hashCode() const {
            return std::hash{}(ms) ^ 2; } };

int main(int iArgs, char * args[]) {

    SetOfBPtrs setOfBPointers;
    setOfBPointers.insert(new B01());
    setOfBPointers.insert(new B02());

    SetOfB setOfB01;
    setOfB01.insert(B01());

    SetOfB setOfB02;
    setOfB02.insert(B02());

    return 0; };


我必须为我目前拥有的每个'Bxx`添加`template <> struct hash `,所以它仍然非常繁琐.但是,嘿,谢谢你的尝试和分享!
谢谢.我以为我只是愚蠢无法找到一种方法来优雅地解决这个问题.知道某些语言没有这个问题也很好.我可以以某种方式安息吧.:)

3> Walter..:

您正在寻找的基于SFINAE的方法需要部分专业化std::hash.如果您的类Bxx是模板(如果它们是从CRTP基础派生的话),则可以执行此操作.例如(注意在编辑中充实)

#include 
#include 
#include 

template
struct B {
  B(int i) : x(i) {}
  std::size_t hashCode() const
  {
    std::cout<<"B::hashCode(): return "<().hashCode())> 
using enable_if_has_hashCode = T;

namespace std {
  template class T, typename... As> 
  struct hash>> 
  {
    std::size_t operator()(const T& x) const
    { return x.hashCode(); }
  };
  // the following would not work, as its not a partial specialisation
  //    (some compilers allow it, but clang correctly rejects it)
  // tempate
  // struct hash>
  // { /* ... */ }; 
}

int main()
{
  using B00 = B;
  B00 b(42);
  std::unordered_set set;
  set.insert(b);
}

生成(在MacOS上使用clang ++)

B :: hashvalue():返回42

另见我的类似问题的相关答案.

然而,概念是解决这类问题的未来之路.

推荐阅读
LEEstarmmmmm
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有