最近都在学Linux系统编程,用C就足矣,有段时间没碰C++了,于是实现些算法练手。

实现多项式乘法的时候发现有几项没有合并同类项,最终调试到这一步时发现了问题。

res是map类型,用find查找key为1991的key-value时,结果得到的却是<12,1>的key-value。

于是转去看那段代码,发现了问题。因为map默认是升序排列,我最后需要打印的多项式是按照幂次数(即这里res的key)降序排列,所以我需要设置map的第3个模板参数,但是由于代码补全我没确认就选择了。

map<int, int, std::greater_equal<int>> res;

我的本意是用std::greater<int>,结果补全的时候没仔细看,补全成了greater_equal<int>,也就是大于或等于。

C++的map的常见实现是内部维护了一颗红黑树(二叉平衡树)从而得到按照key的大小排列的key-value,因为二叉平衡树默认是左子节点<父节点<右子节点,而怎么比较节点之间的大小则是个问题,因为节点可以是类而不是基本数据类型(int、double等等),于是就有了map第3个模板参数,默认是less<>,也就是对基本数据类型来说是,而对类(设为Object)来说则是它的bool operator < (const Object&) const方法。

所以关键是对象之间的operator < 的定义。类似地,在进行find()等需要查找的操作时,取决于对象之间的operator == 的定义

gdb跟踪find()函数的运行过程到达关键代码

      template<typename _Key, typename _Val, typename _KeyOfValue,
typename _Compare, typename _Alloc>
typename _Rb_tree<_Key, _Val, _KeyOfValue,
_Compare, _Alloc>::iterator
_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
find(const _Key& __k)
{
iterator __j = _M_lower_bound(_M_begin(), _M_end(), __k);
return (__j == end()
(gdb)
|| _M_impl._M_key_compare(__k,
_S_key(__j._M_node))) ? end() : __j;
}

含义很好理解,通过_M_lower_bound函数返回迭代器__j,然后若__j为end()或者对__k(插入节点)和__j(下界节点)的节点值的比较为真,则返回end(),否则返回__j。_M_key_compare即我们传入map的第三个模板参数,后面记为operator < (),对int默认定义是:less<int>,即符合以下规则

operator < (1,2):真;operator < (1,1):假;operator < (1,0):假。

_M_lower_bound可以参照STL算法lower_bound的定义(http://www.cplusplus.com/reference/algorithm/lower_bound/)

Returns an iterator pointing to the first element in the range [first,last) which does not compare less than val.

返回按照operator<()排好的升序数组中第1个不小于val的数,也就是第1个operator < (数组元素,待查找值)为假的数,示例如下:

【case 1】数组:1 2 4 5;查找:3。   1<3:真;2<3:真;4<3:假。 ——返回4,对应:查找失败

【case 2】数组:1 2 3 5;查找:3。   1<3:真;2<3:真;3<3:假。 ——返回3,对应:查找成功

回顾刚才的代码,我们可以看到_M_impl._M_key_compare(__k, _S_key(__j._M_node))的意义在哪

operator < (查找值,lower_bound的返回值)为真对应的是上面的case 1,也就是查找失败,find()查找失败会返回end()

回到map上来,也就是说,map调用find()方法不需要operator==的定义,只需要operator<的定义即可。

那么假如operator<被定义为less_equal而不是less呢?

case 1不变,我们重新考虑上面的case 2:1<3:真;2<3:真;3<3:真;5<3:假,返回5。

——等等,3<3为什么为真?注意,此时的<已经不是数学意义上的小于(<)了,而是调用了operator<(),operator<()被赋予数学意义上的小于或等于(<=)的意义,那么3<3的结果就等同于数学意义上的3<=3。

也就是说,模板参数设为less_equal时,lower_bound永远不会返回和查找值一样的值,也就是说,find()函数永远不会返回end(),即查找失败。

(greater_equal和less_equal类似,只不过升序改成降序)

最后给个测试程序来证明我的结论

#include <iostream>
#include <map>
#include <functional>
using namespace std; int main()
{
map<int, int, greater_equal<int>> m;
int key = 250;
m.emplace(key, 0); for (int val = 100; val < 120; val++)
{
// 若m中不存在key则将<key,val>添加进去
if (m.find(key) == m.end())
m.emplace(key, val);
} // 显示map的数据
for (auto& x : m)
cout << x.first << "=>" << x.second << endl; return 0;
}
$ g++ test.cpp -std=c++
$ ./a.out
=>
=>
=>
=>
=>
=>
=>
=>
=>
=>
=>
=>
=>
=>
=>
=>
=>
=>
=>
=>
=>

map中一个key对应尽可能多个value,这就是使用less_equal或greater_equal作为map第3个模板参数的下场了

STL的map容器将第3个模板参数设为less_equal或greater_equal会怎样?的更多相关文章

  1. C++ STL 中 map 容器

    C++ STL 中 map 容器 Map是STL的一个关联容器,它提供一对一(其中第一个可以称为关键字,每个关键字只能在map中出现一次,第二个可能称为该关键字的值)的数据 处理能力,由于这个特性,它 ...

  2. C++STL中map容器的说明和使用技巧(杂谈)

    1.map简介 map是一类关联式容器.它的特点是增加和删除节点对迭代器的影响很小,除了那个操作节点,对其他的节点都没有什么影响.对于迭代器来说,可以修改实值,而不能修改key. 2.map的功能 自 ...

  3. STL之map容器的详解

    一.关于map的介绍 map是STL的 一个容器,和set一样,map也是一种关联式容器.它提供一对一(其中第一个可以称为关键字,每个关键字只能在map中出现一次,第二个可能称为该关键 字的值)的数据 ...

  4. 使用C++STL的map容器实现一种命令映射

    因为最近在练习写一个ftp的服务器,其中的命令有很多种,每个命令对应一个执行函数,能够想到的最简单的实现方式便是使用if--else匹配命令和执行对应的函数,如下所示: if(strcmp(" ...

  5. stl之map容器的原理及应用

    容器的数据结构同样是采用红黑树进行管理,插入的元素健位不允许重复,所使用的节点元素的比较函数,只对元素的健值进行比较,元素的各项数据可通过健值检索出来.map容器是一种关联容器,实现了SortedAs ...

  6. STL——序列式容器

    一.容器概述与分类 1. STL容器即是将运用最广的一些数据结构实现出来.常用的数据结构有array, list, tree, stack, queue, hash table, set, map…… ...

  7. c++ map容器使用及问题

    C++ STL库map容器一些总结,欢迎大家指正补充. map容器由两部分组成,分别为关键字(Key)和值(Value),关键字和值都可以声明为任意类型的数据,注意:关键字唯一,不能重复!使用需包含头 ...

  8. STL中map与hash_map容器的选择收藏

    这篇文章来自我今天碰到的一个问题,一个朋友问我使用map和hash_map的效率问题,虽然我也了解一些,但是我不敢直接告诉朋友,因为我怕我说错了,通过我查询一些帖子,我这里做一个总结!内容分别来自al ...

  9. STL --> map容器

    map容器 一.map简介 map是一类关联式容器.它的特点是增加和删除节点对迭代器的影响很小,除了那个操作节点,对其他的节点都没有什么影响.对于迭代器来说,可以修改实值,而不能修改key. 二.ma ...

随机推荐

  1. How to make a custom WIDGET in OpenERP

    转自:http://sahotaparamjitsingh.blogspot.com/2012/04/how-to-make-custom-widget-in-openerp.html   Hello ...

  2. asp.net MVC 视图文件(cshtml/vbhtml)变更编译过程示范

    更改cshtml文件的时候 并不会触发程序重新启动,而是进入了编译状态  csc.exe进程启动. 非阻塞的方式进行等待,延时等待. 示范程序:http://pan.baidu.com/s/1skDY ...

  3. Linux异步IO操作

    Linux® 中最常用的输入/输出(I/O)模型是同步 I/O.在这个模型中,当请求发出之后,应用程序就会阻塞,直到请求满足为止.这是很好的一种解决方案,因为调用应用程序在等待 I/O 请求完成时不需 ...

  4. iis 设置了主机名 就不能访问

    主机名就是域名,设置这个主要用来防止别人通过IP访问,对于服务器来说多少会更安全点,不过如果没有域名,则设置后无法访问   追答 如果想要测试域名,则可以修改hosts文件实现,这样就可以设置主机名, ...

  5. EMQ 学习---MQTT消息QoS

    MQTT发布消息QoS保证不是端到端的,是客户端与服务器之间的.订阅者收到MQTT消息的QoS级别,最终取决于发布消息的QoS和主题订阅的QoS. 客户端连接: 客户端完成TCP三次握手之后,还需要发 ...

  6. BZOJ 1603 [Usaco2008 Oct]打谷机 dfs

    题意:id=1603">链接 方法:暴力 解析: 搜1到n路径上的边权异或和-. 这几个水题刷的我有点-.. 代码: #include <cstdio> #include ...

  7. C++代码优化方法总结

    C++代码优化方法总结 优化是一个非常大的主题,本文并不是去深入探讨性能分析理论,算法的效率,况且我也没有这个能力.我只是想把一些可以简单的应用到你的C++代码中 的优化技术总结在这里,这样,当你遇到 ...

  8. 分布式ID生成方案

    系统唯一ID是设计一个系统的时候常常会遇到的问题,也常常为这个问题而纠结. 生成ID的方法有很多,适应不同的场景.需求以及性能要求.所以有些比较复杂的系统会有多个ID生成的策略. 0. 分布式ID要求 ...

  9. Lucene用法10个小结 (zhuan)

    http://www.cfanz.cn/index.PHP?c=article&a=read&id=303149 *********************************** ...

  10. EAV/ESS 8.x 自定义服务器正确方法+更新服务器列表

    下面用64位的ESET Smart Security 8.0.319.1进行设置自定义更新服务器说明(注:修改方法32位和64位通用) 1.  让我们先看一下ESET Smart Security 8 ...