如何以标准优雅方式在存储Iterator的容器中使用count/contains处理ConstIterator
先看这段代码:
#include <vector> #include <set> using Iterator = std::vector<int>::iterator; using ConstIterator = std::vector<int>::const_iterator; std::set<ConstIterator> s; int main() { bool equal = Iterator{} == ConstIterator{}; s.count(Iterator{}); s.count(ConstIterator{}); s.contains(Iterator{}); s.contains(ConstIterator{}); }
这段代码是合法的,因为ConstIterator可由Iterator隐式构造,但反之不行。若将s的类型改为std::set<Iterator>,传入ConstIterator的count和contains调用会编译失败。
我们知道count和contains均不会修改集合,内部仅做值的相等比较,且Iterator与ConstIterator的相等判断本身完全合法(如代码中equal变量的定义)。用std::ranges::find_if配合lambda是一种可行替代方案:
std::ranges::find_if(s, [target = ConstIterator{}](auto v) { return v == target; });
但核心问题是:当持有ConstIterator,需要在存储Iterator的容器中调用count/contains时,有没有标准且优雅的实现方式?
标准优雅的实现方式
1. 利用透明比较器(C++20及以上)
std::set默认的std::less<T>在C++20开始支持透明比较(即允许不同类型的参数比较,只要类型间能执行<运算)。我们可以将集合的比较器改为透明版本的std::less<>,让std::set<Iterator>直接接受ConstIterator作为参数:
std::set<Iterator, std::less<>> s;
修改后,s.count(ConstIterator{})和s.contains(ConstIterator{})均可正常编译。std::less<>会直接对两种迭代器执行<运算,而它们的等价关系(!(a < b) && !(b < a))能正确对应到迭代器的相等判断。
2. 显式转换(仅安全场景下使用)
若能确保ConstIterator指向的是同一非const容器的元素,可通过const_cast显式转换:
auto it = const_cast<Iterator>(const_it); s.count(it);
注意:如果ConstIterator来自const容器或指向const对象,const_cast会导致未定义行为,仅推荐在完全可控的场景下使用。
3. 封装find_if为通用函数(兼容旧标准)
若需兼容C++20之前的版本,可将find_if的逻辑封装为通用函数,让代码更简洁:
template <typename Container, typename T> bool contains(const Container& c, const T& value) { return std::find_if(c.begin(), c.end(), [&value](const auto& elem) { return elem == value; }) != c.end(); } // 调用示例 bool exists = contains(s, ConstIterator{});
内容的提问来源于stack exchange,提问作者αλεχολυτ

