如何在boost::multi_index_container中获取索引值不等于指定值的迭代器
首先得明确一点:boost::multi_index的哈希索引(hashed_non_unique/hashed_unique)是基于哈希桶存储的,本身不支持直接的"不等于某值"的范围查询——因为哈希索引没有按照键的大小排序,它的核心是快速定位哈希值相同的元素,也就是等于某个值的元素集合。
你之前的代码错误原因
你尝试使用的equal_range重载版本:
std::pair<iterator,iterator> equal_range( const CompatibleKey& x, const CompatibleHash& hash, const CompatiblePred& eq)const;
这个重载的作用不是用来做反向匹配的,它的设计目的是:当你不想用索引默认的哈希/比较函数时,传入自定义的哈希函数和相等比较函数,来查找等于x的元素。所以你的lambda写法完全不符合这个重载的要求,才会出现编译错误:
- 第一个参数不是迭代器,而是要查找的键值(这里是
0),你错误地传了m_mappingDb->begin() - 这个重载要求的
CompatiblePred是判断两个键是否相等的函数(接受两个参数返回bool),但编译器报错是因为你调用的参数顺序不对,而且这个重载根本不是用来实现"不等于"逻辑的
正确的实现方式
既然哈希索引没法直接做非等值范围查询,我们可以换个思路:先拿到所有等于0的元素范围,然后遍历索引中除了这个范围之外的所有元素。这样比全遍历稍微高效一点,因为跳过了中间已经确定是0的部分。
代码示例:
// 获取目标哈希索引 auto& idx = m_mappingDb->get<tags::myIndex>(); // 拿到所有键为0的元素范围 auto zero_elements = idx.equal_range(0); // 遍历第一部分:从索引开头到第一个0元素 for (auto it = idx.begin(); it != zero_elements.first; ++it) { // 处理非0的元素 process_element(*it); } // 遍历第二部分:从最后一个0元素的下一个到索引结尾 for (auto it = zero_elements.second; it != idx.end(); ++it) { // 处理非0的元素 process_element(*it); }
关于你提到的替代方案的问题
你之前想用lower_bound(1)和upper_bound(std::numeric_limits<uint32_t>::max())的方法是完全不可行的——因为哈希索引的lower_bound和upper_bound并不是按照键的数值大小来排序的,它们的顺序是基于哈希桶的内部顺序,和键的实际大小没有关系。用这个方法得到的结果完全不符合预期,会漏掉很多非0元素或者包含0元素,一定要避免。
优化建议
如果你的业务中经常需要做这种"不等于某值"的查询,或者需要基于键的数值范围查询,建议给multi_index_container添加一个ordered_non_unique类型的索引。有序索引是按照键的大小排序的,此时你可以用lower_bound(1)和end()来直接获取所有大于等于1的元素(也就是非0元素),代码会更简洁高效:
// 假设你的容器定义中添加了ordered索引 struct tags { struct myHashedIndex; struct myOrderedIndex; // 新增的有序索引 }; using MappingDb = boost::multi_index_container< YourElementType, boost::multi_index::indexed_by< boost::multi_index::hashed_non_unique< boost::multi_index::tag<tags::myHashedIndex>, boost::multi_index::member<YourElementType, uint32_t, &YourElementType::your_key> >, boost::multi_index::ordered_non_unique< boost::multi_index::tag<tags::myOrderedIndex>, boost::multi_index::member<YourElementType, uint32_t, &YourElementType::your_key> > > >; // 查询非0元素时用有序索引: auto& ordered_idx = m_mappingDb->get<tags::myOrderedIndex>(); auto non_zero_range = std::make_pair(ordered_idx.lower_bound(1), ordered_idx.end()); for (auto it = non_zero_range.first; it != non_zero_range.second; ++it) { process_element(*it); }
内容的提问来源于stack exchange,提问作者Niklas

