map
类模板原型
map是一个类模板,实例化时需要提供以下的模板实参。
template <typename _Key,
typename _Tp,
typename _Compare = std::less<_Key>,
typename _Alloc = std::allocator<std::pair<const _Key, _Tp> > >
class map
{
...
}
_Key :键的类型,无默认值,是必需的。
_Tp :值的类型,无默认值,是必需的。
**_Compare ** :比较(仿)函数,有默认值。当键_Key是int、double、bool等内置类型时,将调用stl_function.h中预定义的std::less类模板;当键_Key是自定义类型,需要传入自定义类型的比较函数。
_Alloc :空间分配器,有默认值,进行空间的分配与回收,一般使用默认。
模板实参
使用unordered_set存储自定义类型的键时,至少需要提供3个模板实参:
- 存储的自定义的键类型_Value
- 值的类型_Tp
- 比较函数_Compare
源代码
错误示例
#include <iostream>
#include <map>
using namespace std;
struct MyPair{
int x_;
int y_;
MyPair(int x, int y):x_(x),y_(y){
}
bool operator<(const MyPair &larger) const{
return y_ <= larger.y_; //这里错误地使用 <=
}
};
int main(){
map<MyPair, //typename _Key
int> //typename _Tp
umap;
umap[MyPair(1,1)] = 1;
umap[MyPair(1,1)] = 1;
umap[MyPair(2,2)] = 2;
umap.emplace(MyPair(2,2),2);
for(const auto &it: umap)
cout << it.first.x_ <<","<< it.first.y_ << "," << it.second << endl;
}
错误示例的控制台结果
1,1,1
1,1,1
2,2,2
2,2,2
可以看到map中的元素重复了,这是因为提供的比较函数不满足严格弱排序。比较(仿)函数Comp可以视作二元关系R,需要满足以下3个特点:
- 反自反关系:R是A上的反自反关系⇔∀a∈A→¬(aRa)),即Comp(x,x)的返回值为false;
- 反对称关系:R是A上的反对称关系⇔∀a,b∈A, aRb ^ bRa → a=b,即Comp(a,b)与Comp(b,a)的返回值同为false时(或者同为true时),a=b;
- 传递性:对∀a,b∈A,c∈A,当aRb,bRc时,有aRc,即Comp(a,b)与Comp(b,c)的返回值同为true时,Comp(a,c)的返回值也为true;
Comp(a,b)与Comp(b,c)的返回值同为false时,Comp(a,c)的返回值也为false;
从上面可以看出,小于运算符是满足以上三个条件的;而小于等于运算符满足反对称关系和传递性,但是不满足反自反关系。所以在MyPair的小于运算符函数体中,应该使用满足严格弱排序的小于运算符(或者大于运算符)。
正确示例
常见的做法有两种,一种是提供比较函数;另一种是在自定义类型中重载小于运算符(也是函数的一种),这两种方法都需要函数满足严格弱排序。
#include <iostream>
#include <map>
using namespace std;
struct MyPair{
int x_;
int y_;
MyPair(int x, int y):x_(x),y_(y){
}
// //第一种方法,自定义键中重载小于运算符,切记严格弱排序,函数参数与成员函数均为const
// bool operator<(const MyPair &larger) const{
// return y_ > larger.y_;
// }
};
//第二种方法,提供比较仿函数,map的类模板实参配合map的构造函数有多种写法,文章【unordered_set存储自定义类型】中已经列举过,不再重复
struct Comp{
bool operator()(const MyPair& smaller, const MyPair& larger)const{
return smaller.y_ < larger.y_;
}
};
int main(){
map<MyPair, //typename _Key
int, //typename _Tp
Comp> //typename _Compare
umap;
umap[MyPair(1,1)] = 1;
umap[MyPair(1,1)] = 1;
umap[MyPair(2,2)] = 2;
umap.emplace(MyPair(2,2),2); //emplace原地构造
for(const auto &it: umap)
cout << it.first.x_ <<","<< it.first.y_ << "," << it.second << endl;
}
控制台结果
1,1,1
2,2,2
结论:
- 在理解unordered_set和map的类模板后,遇到没有提及的unordered_map、set、priority_queue、pair也可以通过查看其类模板和构造函数,来了解应该提供的模板实参和构造函数需要的参数,所以第二种方法的三种形式也不再赘述;
- 在遇到模板实参是 std::less类模板时,如果需要使用自定义类型,都是传入严格弱排序的仿函数,比如中的sort函数和priority_queue的模板实参。