如何为unordered_map的std::set类型值传递lambda比较器?
如何为unordered_map的value类型(带lambda比较器的std::set)传递比较器?
你碰到的问题本质很明确:lambda闭包类型的默认构造函数是被编译器删除的,而unordered_map的operator[]有个关键行为——当你访问的键不存在时,它会自动默认构造一个对应的value对象。但你的MySet类型(也就是std::set<string, decltype(SetCompare)>)依赖lambda作为比较器,根本无法默认构造,这就是报错的直接原因。
下面给你几个实用的解决方案,按推荐程度排序:
方案1:替换lambda为自定义可默认构造的比较器结构体(最优选择)
把lambda转换成一个普通的结构体,让它拥有编译器自动生成的默认构造函数,这样MySet就可以被默认构造,operator[]也能正常使用了:
struct SetCompare { bool operator()(const std::string& a, const std::string& b) const { const size_t L = a.length(); const size_t R = b.length(); return (L == R) ? (a < b) : (L < R); } }; // 现在MySet的比较器是可默认构造的结构体 using MySet = std::set<std::string, SetCompare>; std::unordered_map<std::string, MySet> Map; // 直接用operator[]完全没问题 Map["abc"].insert("hello"); Map["xyz"].insert("mapped to xyz");
这个方案没有任何性能开销,代码可读性也强,是最推荐的做法。
方案2:避免使用operator[],改用emplace/insert + at()操作
如果你不想修改比较器的定义,那可以绕过operator[]的默认构造逻辑,直接手动构造带比较器的MySet插入到map中,之后用at()方法访问已存在的键(at()不会默认构造对象,只会查找已存在的键,不存在会抛异常):
// 保留你原来的lambda和MySet定义 auto SetCompare = [](const std::string& a, const std::string& b) { const size_t L = a.length(); const size_t R = b.length(); return (L == R) ? (a < b) : (L < R); }; using MySet = std::set<std::string, decltype(SetCompare)>; std::unordered_map<std::string, MySet> Map; // 直接在map中构造带比较器的MySet Map.emplace("abc", MySet(SetCompare)); // 或者先创建对象再插入 MySet mySet(SetCompare); mySet.insert("test"); Map.insert({"pqr", mySet}); // 访问时用at(),避免触发默认构造 Map.at("abc").insert("another element");
这个方案适合你不想改动原有比较器逻辑的场景,缺点是不能直接用operator[]快速访问,需要确保键已存在。
方案3:用std::function包装lambda(灵活但有性能开销)
如果你既想保留lambda的写法,又想使用operator[],可以用std::function把lambda包装成一个可默认构造的类型。不过要注意,std::function是多态包装器,会带来一点性能损耗:
// 用std::function包装lambda std::function<bool(const std::string&, const std::string&)> SetCompare = [](const std::string& a, const std::string& b) { const size_t L = a.length(); const size_t R = b.length(); return (L == R) ? (a < b) : (L < R); }; using MySet = std::set<std::string, decltype(SetCompare)>; std::unordered_map<std::string, MySet> Map; // 现在operator[]可以用了,但默认构造的MySet的比较器是空的,所以需要手动赋值或构造 Map["abc"] = MySet(SetCompare); Map["abc"].insert("test element"); // 或者直接emplace构造,更高效 Map.emplace("xyz", MySet(SetCompare)); Map["xyz"].insert("mapped to xyz");
这个方案灵活性最高,但因为std::function的间接调用,性能会比前两个方案稍差一点,适合对性能要求不高的场景。
内容的提问来源于stack exchange,提问作者Ajay
相关产品推荐
相关产品推荐

