一. 问题背景
1.1. 问题描述
朋友今天问了一个关于 map 的问题,假设有个 Person 结构体如下:
typedef struct {
std::string name;
int age;
} Person;
map 定义如下:
typedef std::map<Person, int> ScoreMap;
直接 map[Person] = int 会报错:
/usr/include/c++/5/bits/stl_function.h:387:20: error: no match for ‘operator<’ (operand types are ‘const Person’ and ‘const Person’)
{ return __x < __y; }
1.2. 问题分析
出错的原因是编译器不知道 key 插入 map 的顺序,你需要定义 operator <
。
既然知道了原因,我们来着手解决吧。
二. 解决方案
我们先来看下 map 模板定义:
template < class Key, // map::key_type
class T, // map::mapped_type
class Compare = less<Key>, // map::key_compare
class Alloc = allocator<pair<const Key,T> > // map::allocator_type
> class map;
2.1. 方案一
// 定义 operator <
bool operator< (const Person& p1, const Person& p2) {
return std::make_pair(p1.name, p1.age) < std::make_pair(p2.name, p2.age);
}
typedef std::map<Person, int> ScoreMap;
void find(ScoreMap *m, Person *p) {
auto it = m->find(*p);
if (it != m->end()) {
std::cout << "Found! name=" << it->first.name << ", age="
<< it->first.age << ", score=" << it->second << "\n";
} else {
std::cout << "Not found, {" << p->name << ", " << p->age << "}\n";
}
}
int main() {
ScoreMap m;
Person p1{"Aland", 18};
m[p1] = 100;
Person p2{"Aland", 18};
find(&m, &p2);
Person p3{"Jack", 20};
find(&m, &p3);
return 0;
}
输入结果如下:
Found! name=Aland, age=18, score=100
Not found, {Jack, 20}
思考: 方案一有什么缺点么?
- map 的 key 是结构体,如果结构体很大的话,map 的内存占用也会很大。
如果 key 使用结构体指针的话,map 的内存占用会小很多。接下来我们看看第二种方案。
2.2. 方案二
思考: 指针作为 map 的 key 有什么缺点么?
- 由于 key 是结构体指针,在 map 进行 find 时只能得到指针,不能直接判断是否是我们要查找的 key。
那我们怎么办?
仔细观察可以发现 map 的模板定义中,第 3 个参数是关于 compare 的。
The expression comp(a,b), where comp is an object of this type and a and b are key values, shall return true if a is considered to go before b in the strict weak ordering the function defines.
Two keys are considered equivalent if key_comp returns false reflexively (i.e., no matter the order in which the keys are passed as arguments).
注意: 如果 map 的两个 key a 和 b 相等,则返回 false。
知道了这个,那我们就可以着手去写比较函数了,比较的内容是两个结构体指针的内容。
struct comp {
bool operator()(const Person *p1, const Person *p2) const {
return !(p1->name == p2->name && p1->age == p2->age);
}
};
typedef std::map<Person *, int, comp> ScoreMap;
void find(ScoreMap *m, Person *p2) {
auto it = m->find(p2);
if (it != m->end()) {
std::cout << "Found! name=" << it->first->name << ", age="
<< it->first->age << ", score=" << it->second << "\n";
} else {
std::cout << "Not found, {" << p2->name << ", " << p2->age << "}\n";
}
}
int main() {
ScoreMap m;
Person p1{"Aland", 18};
m[&p1] = 100;
Person p2{"Aland", 18};
find(&m, &p2);
Person p3{"Jack", 20};
find(&m, &p3);
return 0;
}
map 调用 find 函数时会使用 comp 比较 key 和传入的指针。