相关内容: C++ STL: map自定义键值类型
本文目录
1. unordered_map的定义
下面是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
-
第1个参数,存储key值。
-
第2个参数,存储mapped value。
-
第3个参数,为哈希函数的函数对象。它将key作为参数,并利用函数对象中的哈希函数返回类型为size_t的唯一哈希值。默认值为std::hash<key>。
-
第4个参数,为等比函数的函数对象。它内部通过等比操作符’=='来判断两个key是否相等,返回值为bool类型。默认值是std::equal_to<key>。在unordered_map中,任意两个元素之间始终返回false。
2. 问题分析
对于unordered_map而言,当我们插入<key, value>的时候,需要哈希函数的函数对象对key进行hash,又要利用等比函数的函数对象确保插入的键值对没有重复。然而,当我们自定义类型时,c++标准库并没有对应的哈希函数和等比函数的函数对象。因此需要分别对它们进行定义。
因为都是函数对象,它们两个的实际定义方法并没有很大差别。不过后者比前者多了一个方法。因为等比函数的函数对象默认值std::equal_to<key>内部是通过调用操作符"=="进行等值判断,因此我们可以直接在自定义类里面进行operator==()重载(成员和友元都可以)。
因此,如果要将自定义类型作为unordered_map的键值,需如下两个步骤:
-
定义哈希函数的函数对象;
-
定义等比函数的函数对象或者在自定义类里重载operator==()。
3. 定义方法
本文所有案例在用g++编译时,需加上-std=c++11或者-std=c++0x;如果用VS编译,请选择2010年及以上版本。
为了避免重复,下文以讨论哈希函数的函数对象为主,参数4则是通过直接在自定义类里面对operator==()进行重载。
首先简要介绍一下函数对象的概念:在《C++ Primer Plus》里面,函数对象是可以以函数方式与()结合使用的任意对象。这包括函数名、指向函数的指针和重载了“operator()”操作符的类对象。基于此,我们提出3个方法。
3.1 方法1:std::function
方法1就是利用std::function为person_hash()构建函数实例。初始化时,这个函数实例就会被分配那个指向person_hash()的指针(通过构造函数实现),如下所示。
#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 );
//OK: unordered_map<Person, int, decltype(&person_hash)> ids(100, person_hash );
unordered_map<Person,