STL的map容器將第3個模板參數設為less_equal或greater_equal會怎樣?
最近都在學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()函數的運行過程到達關鍵代碼
2288 template<typename _Key, typename _Val, typename _KeyOfValue, 2289 typename _Compare, typename _Alloc> 2290 typename _Rb_tree<_Key, _Val, _KeyOfValue, 2291 _Compare, _Alloc>::iterator2292 _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>:: 2293 find(const _Key& __k) 2294 { 2295 iterator __j = _M_lower_bound(_M_begin(), _M_end(), __k); 2296 return (__j == end() (gdb) 2297 || _M_impl._M_key_compare(__k, 2298 _S_key(__j._M_node))) ? end() : __j; 2299 }
含義很好理解,通過_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++11 $ ./a.out 250=>119 250=>118 250=>117 250=>116 250=>115 250=>114 250=>113 250=>112 250=>111 250=>110 250=>109 250=>108 250=>107 250=>106 250=>105 250=>104 250=>103 250=>102 250=>101 250=>100 250=>0
map中一個key對應盡可能多個value,這就是使用less_equal或greater_equal作為map第3個模板參數的下場了
STL的map容器將第3個模板參數設為less_equal或greater_equal會怎樣?