STL unordered_map 自定义键值类型

问题分析

对于unordered_map而言,当我们插入<key, value>的时候,需要哈希函数的函数对象对key进行hash,又要利用等比函数的函数对象确保插入的键值对没有重复。然而,当我们自定义类型时,c++标准库并没有对应的哈希函数和等比函数的函数对象。因此需要分别对它们进行定义。

STL unordered_map 官方定义

template<class Key,
    class Ty,
    class Hash = std::hash<Key>,
    class Pred = std::equal_to<Key>,
    class Alloc = std::allocator<std::pair<const Key, Ty> > >
    class unordered_map;
    > class unordered_map
  • 第一个参数 key 值
  • 第二个参数 mapped value
  • 第三个参数 存储哈希函数的函数对象,并利用函数对象中的哈希函数返回类型为size_t的唯一哈希值
  • 第四个参数 等比函数的函数对象。它内部通过等比操作符’=='来判断两个key是否相等,返回值为bool类型

重载方法

  • 构建函数实例
#include <iostream>
#include <unordered_map>
#include <string>
#include <functional>

using namespace std;

class Person{
public:
    string name;
    int age;

    Person(string n, int a){
        name = n;
        age = a;
    }
    bool operator==(const Person & p) const 
    {
       return name == p.name && age == p.age;
    }
};

size_t person_hash( const Person & p ) 
{
    return hash<string>()(p.name) ^ hash<int>()(p.age);
}

int main(int argc, char* argv[])
{
    //ERRO: unordered_map<Person,int,decltype(&person_hash)> ids;
    //ERRO: unordered_map<Person,int,person_hash> ids(100, person_hash );
    unordered_map<Person, int, decltype(&person_hash)> ids(100, person_hash);  ////需要把person_hash传入构造函数
    // C++11新增的关键字decltype。它可以直接获取自定义哈希函数的类型,并把它作为参数传送。
    ids[Person("Mark", 17)] = 40561;
    ids[Person("Andrew",16)] = 40562;
    for ( auto ii = ids.begin() ; ii != ids.end() ; ii++ )
        cout << ii->first.name 
        << " " << ii->first.age 
        << " : " << ii->second 
        << endl;
        return 0;
}
  • 重载operator() 类
    利用重载operator()的类,将哈希函数打包成可以直接调用的类
#include <iostream>
#include <string>
#include <unordered_map>
#include <functional>
using namespace std;

class Person{
public:
    string name;
    int age;

    Person(string n, int a){
        name = n;
        age = a;
    }

    bool operator==(const Person & p) const 
    {
        return name == p.name && age == p.age;
    }
};

struct hash_name{
    size_t operator()(const Person & p) const{
        return hash<string>()(p.name) ^ hash<int>()(p.age);
    }
};

int main(int argc, char* argv[]){
    unordered_map<Person, int, hash_name> ids; //不需要把哈希函数传入构造器
    ids[Person("Mark", 17)] = 40561;
    ids[Person("Andrew",16)] = 40562;
    for ( auto ii = ids.begin() ; ii != ids.end() ; ii++ )
        cout << ii->first.name 
        << " " << ii->first.age
        << " : " << ii->second
        << endl;
    return 0;
}
  • 模板定制
#include <iostream>
#include <unordered_map>
#include <string>
#include <functional>
using namespace std;

typedef pair<string,string> Name;

namespace std {
    template <> //function-template-specialization
        class hash<Name>{
        public :
            size_t operator()(const Name &name ) const
            {
                return hash<string>()(name.first) ^ hash<string>()(name.second);
            }
    };
};

int main(int argc, char* argv[])
{
    unordered_map<Name,int> ids;
    ids[Name("Mark", "Nelson")] = 40561;
    ids[Name("Andrew","Binstock")] = 40562;
    for ( auto ii = ids.begin() ; ii != ids.end() ; ii++ )
        cout << ii->first.first 
             << " " << ii->first.second 
             << " : " << ii->second
             << endl;
    return 0;
}

当我们将模板订制包含在定义类的头文件中时,其他人无需额外工作,就可以直接用我们的类作为任何无序容器的键。这对于要使用我们自定义类的人来说,绝对是最方便的。如果你想要在多个地方用这个类,方法3是最好的选择。当然,你要确保自己的hash function不会影响std空间里的其他类。

#include <iostream>
#include <string>
#include <unordered_map>
#include <functional>
using namespace std;

class Person{
public:
    string name;
    int age;

    Person(string n, int a){
        name = n;
        age = a;
    }
};

namespace std{
    template<>
    struct hash<Person>{//哈希的模板定制
    public:
        size_t operator()(const Person &p) const 
        {
            return hash<string>()(p.name) ^ hash<int>()(p.age);
        }

    };

    template<>
    struct equal_to<Person>{//等比的模板定制
    public:
        bool operator()(const Person &p1, const Person &p2) const
        {
            return p1.name == p2.name && p1.age == p2.age;
        }

    };
}

int main(int argc, char* argv[]){
    unordered_map<Person, int> ids;
    ids[Person("Mark", 17)] = 40561;
    ids[Person("Andrew",16)] = 40562;
    for ( auto ii = ids.begin() ; ii != ids.end() ; ii++ )
        cout << ii->first.name 
        << " " << ii->first.age
        << " : " << ii->second
        << endl;
    return 0;
}

原文链接: https://www.cnblogs.com/wsl-hitsz/p/14335878.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;

也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬

    STL  unordered_map 自定义键值类型

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/397368

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年4月10日 上午9:30
下一篇 2023年4月10日 上午9:30

相关推荐