带自定义比较器的std::map如何让下游用户无需关注比较器即可使用?
问题:带自定义比较器的std::map如何让下游无感知使用?
首先给出自定义对象与带比较器的map定义:
struct Obj { int id; std::string data; std::vector<std::string> moreData; }; struct Comparator { using is_transparent = std::true_type; bool operator()(Obj const& obj1, Obj const& obj2) { return obj1.id < obj2.id; }; }; std::map<Obj,int,Comparator> compMap;
需求是让下游用户无需处理该自定义比较器即可正常使用此map,但传入如下模板函数时触发类型错误:
template<class T> inline void add(std::map<T, int>& theMap, T const & keyObj) { auto IT = theMap.find(keyObj); if (IT != theMap.end()) IT->second++; else theMap[keyObj] = 1; } add(compMap,newObj); // 此处触发类型错误
编辑补充:
我之前为简化通用场景忽略了明显方案,将模板修改为:
template<class T, class Comp, class Alloc> inline void add(std::map<T, int, Comp, Alloc>& theMap, T const & keyObj)
但仍存在T无法推导的问题,不过错误已从80个减少到1个,有进展。
解决方案
1. 最简洁的模板推导修复
你修改后的模板出现T无法推导的问题,是因为编译器无法直接将std::map<T, int, Comp, Alloc>中的T与函数第二个参数的T完成关联推导。换个思路,直接让模板推导整个map类型,而非拆分参数:
template<class Map> inline void add(Map& theMap, const typename Map::key_type& keyObj) { auto it = theMap.find(keyObj); if (it != theMap.end()) it->second++; else theMap[keyObj] = 1; }
这种写法完全不关心map的比较器、分配器参数,只要是标准map类型(具备key_type嵌套类型)就能正常推导,下游用户直接调用add(compMap, newObj)即可,完全感知不到自定义比较器的存在。
2. 利用透明比较器的额外优势(可选)
你的比较器已经声明了is_transparent,这意味着可以扩展支持直接用int查找(无需构造完整Obj),只需补充比较器的重载逻辑:
struct Comparator { using is_transparent = std::true_type; bool operator()(const Obj& obj1, const Obj& obj2) { return obj1.id < obj2.id; }; bool operator()(const Obj& obj, int id) { return obj.id < id; }; bool operator()(int id, const Obj& obj) { return id < obj.id; }; };
此时add函数甚至可以直接接受int作为key参数(比如add(compMap, 123)),进一步提升灵活性,下游用户依然无需关心比较器细节。
3. 另一种兼容式模板写法
如果不想依赖Map::key_type,也可以用C++11及以上支持的可变参数模板来覆盖所有map参数:
template<class K, class V, class... ExtraArgs> inline void add(std::map<K, V, ExtraArgs...>& theMap, const K& keyObj) { auto it = theMap.find(keyObj); if (it != theMap.end()) it->second++; else theMap[keyObj] = 1; }
这种写法会自动推导键类型K、值类型V以及后续的比较器、分配器等参数,完美解决之前的推导问题。
内容的提问来源于stack exchange,提问作者Sir Demios
相关产品推荐
相关产品推荐

