map 中结构体或指针做 key

一. 问题背景

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 和传入的指针。

三. 参考

http://www.cplusplus.com/reference/map/map/

https://stackoverflow.com/questions/22248284/error-no-match-for-operator-in-x-y-when-trying-to-insert-in-two-map

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值